From a97f5b87b29ea25824e61cb45ab519d4e7fb149c Mon Sep 17 00:00:00 2001 From: C Beach Date: Wed, 26 Sep 2018 15:49:20 -0500 Subject: [PATCH 01/14] #4 - FIX: B2 Logging level is now controlled from the Settings page and cleaned up --- README.md | 9 +- TESTING.md | 28 + build.gradle | 7 + gradle.properties | 5 +- .../blackboard/common/BbContext.java | 76 +- .../blackboard/common/BbLogger.java | 95 ++ .../integration/blackboard/common/BbUtil.java | 523 ++++---- .../blackboard/common/SqlUtil.java | 346 ++--- .../common/content/ContentUtil.java | 743 +++++------ .../common/content/LegacyItemUtil.java | 317 ++--- .../common/content/PlacementUtil.java | 267 ++-- .../common/content/RegistrationUtil.java | 1042 +++++++-------- .../buildingblock/Configuration.java | 1163 +++++++++-------- .../buildingblock/data/WrappedContent.java | 1115 ++++++++-------- .../buildingblock/data/WrappedCourse.java | 193 +-- .../buildingblock/data/WrappedUser.java | 555 ++++---- .../buildingblock/servlet/ContentServlet.java | 875 ++++++------- .../main/manifests/bb-manifest-unresolved.xml | 18 +- .../properties/version-unresolved.properties | 2 + oeqPrimaryB2/src/main/webapp/admin/config.jsp | 54 +- .../impl/SynchroniseContentThread.java | 320 ++--- 21 files changed, 3960 insertions(+), 3793 deletions(-) create mode 100644 TESTING.md create mode 100644 oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/BbLogger.java create mode 100644 oeqPrimaryB2/src/main/properties/version-unresolved.properties diff --git a/README.md b/README.md index d3e8b43..26a3d93 100644 --- a/README.md +++ b/README.md @@ -42,4 +42,11 @@ TODO ``` ~$ ./gradlew generateGradleLintReport ~$ ./gradlew fixGradleLint -``` \ No newline at end of file +``` + +## Logging +### Self / Managed Hosting +Logs to bb-services.txt. The Building Block Settings page will list the log file as well. + +### SaaS +Logs to Kibana. Search for `oeqInteg`. The default graph won't provide the rows, so hover over the bottom rectangle on the left-hand side of the Kibana Learn interface (should be green) to Add Panel. Select Table and 'selected' queries. diff --git a/TESTING.md b/TESTING.md new file mode 100644 index 0000000..98936c7 --- /dev/null +++ b/TESTING.md @@ -0,0 +1,28 @@ +# Test Scenarios for oeqPrimaryB2 + +## Install Building Block +Ensure the permissions are as expected. + +## Edit Building Block Settings +Ensure you can save / edit configurations and they persist. +Ensure the version on the settings page is the same version as in the war filename. + +## Add New openEQUELLA object +1. Navigate to a Blackboard course +1. Under `Content > Tools` , select `openEQUELLA Object` +1. Ensure the Selection Session displays and you can select / add a link into the course + +## Access New Integration Link + +## Access Existing Integration Link + +## Add openEQUELLA Module + +## Course Tools > openEQUELLA Contribute + +## Course Tools > openEQUELLA My Resources + +## Course Tools > openEQUELLA Search + +## Push-To-LMS +Note: Requires the web service to be installed \ No newline at end of file diff --git a/build.gradle b/build.gradle index 0ab646d..0aeffde 100644 --- a/build.gradle +++ b/build.gradle @@ -80,6 +80,13 @@ allprojects { } into("WEB-INF") } + from ('src/main/properties/version-unresolved.properties'){ // set the B2 version and place in the war + filter{ it.replaceAll('@VERSION@', artifactVersion)} + rename { String fileName -> + fileName.replace("-unresolved", "") + } + into(".") + } from ('src/main/java/icons.xml') { into("WEB-INF/classes") } diff --git a/gradle.properties b/gradle.properties index 3c9ffea..d3de13f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,4 @@ -bbLearnVersion=9.1.201410.160373 -#bbLearnVersion=3400.0.0 +bbLearnVersion=3400.0.0 tomcatVersion=8.0.42 gradleTomcatPluginVersion=2.5 jstlVersion=1.2 @@ -7,5 +6,5 @@ jspApiVersion=2.3.3 servletApiVersion=4.0.1 junitVersion=4.12 # All building blocks / web services are re-versioned when this changes. -artifactVersion=2.0.0-SNAPSHOT +artifactVersion=2.0.1-SNAPSHOT diff --git a/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/BbContext.java b/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/BbContext.java index cbf2e5e..52e1811 100644 --- a/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/BbContext.java +++ b/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/BbContext.java @@ -12,42 +12,42 @@ */ // @NonNullByDefault public class BbContext { - /* @Nullable */ - private static BbContext instance; - private static final Object instanceLock = new Object(); - - private final ContextManager context; - private final BbPersistenceManager bbPm; - - @SuppressWarnings("null") - public static BbContext instance() { - if (instance == null) { - synchronized (instanceLock) { - if (instance == null) { - instance = new BbContext(); - } - } - } - return instance; - } - - @SuppressWarnings("nls") - private BbContext() { - try { - context = ContextManagerFactory.getInstance(); - final VirtualInstallation vi = context.getContext().getVirtualInstallation(); - bbPm = BbPersistenceManager.getInstance(vi); - } catch (Exception e) { - BbUtil.error("Couldn't init BbContext", e); - throw Throwables.propagate(e); - } - } - - public ContextManager getContextManager() { - return context; - } - - public BbPersistenceManager getPersistenceManager() { - return bbPm; - } + /* @Nullable */ + private static BbContext instance; + private static final Object instanceLock = new Object(); + + private final ContextManager context; + private final BbPersistenceManager bbPm; + + @SuppressWarnings("null") + public static BbContext instance() { + if (instance == null) { + synchronized (instanceLock) { + if (instance == null) { + instance = new BbContext(); + } + } + } + return instance; + } + + @SuppressWarnings("nls") + private BbContext() { + try { + context = ContextManagerFactory.getInstance(); + final VirtualInstallation vi = context.getContext().getVirtualInstallation(); + bbPm = BbPersistenceManager.getInstance(vi); + } catch (Exception e) { + BbLogger.instance().logError("Couldn't init BbContext", e); + throw Throwables.propagate(e); + } + } + + public ContextManager getContextManager() { + return context; + } + + public BbPersistenceManager getPersistenceManager() { + return bbPm; + } } diff --git a/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/BbLogger.java b/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/BbLogger.java new file mode 100644 index 0000000..4cc25e9 --- /dev/null +++ b/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/BbLogger.java @@ -0,0 +1,95 @@ +package org.apereo.openequella.integration.blackboard.common; + +import blackboard.platform.log.LogService; +import blackboard.platform.log.LogServiceFactory; + +// Wraps the common logger, and exposes the ability to soft 'change' +// Logging Levels +public class BbLogger { + + private static final String SIG = "oeqInteg - "; + private static final LogService LOGGER = LogServiceFactory.getInstance(); + + // Maybe not the best way to handle this. + private static BbLogger instance; + private static final Object instanceLock = new Object(); + + private LogLevel logLevel = LogLevel.NotSet; + + // If NotSet is chosen, the logger will use the standard logger + // levels for messaging. If any other value is chosen, the logger + // will still honor the log levels, filtered by the level set. + public enum LogLevel { + NotSet, Warn, Info, Debug, SqlTrace, Trace, + } + + @SuppressWarnings("null") + public static BbLogger instance() { + if (instance == null) { + synchronized (instanceLock) { + if (instance == null) { + instance = new BbLogger(); + } + } + } + return instance; + } + + private BbLogger() { + + } + + public synchronized void setLoggingLevel(LogLevel level) { + logLevel = level; + } + + public void logTrace(String msg) { + if (logLevel == LogLevel.NotSet) { + LOGGER.logAudit(SIG + msg); + } else if (logLevel == LogLevel.Trace) { + LOGGER.logWarning(SIG + "Trace - " + msg); + } + } + + public void logSqlTrace(String msg) { + if (logLevel == LogLevel.NotSet) { + LOGGER.logAudit(SIG + msg); + } else if ((logLevel == LogLevel.SqlTrace) || (logLevel == LogLevel.Trace)) { + LOGGER.logWarning(SIG + "SqlTrace - " + msg); + } + } + + public void logDebug(String msg) { + if (logLevel == LogLevel.NotSet) { + LOGGER.logDebug(SIG + msg); + } else if ((logLevel == LogLevel.Debug) || (logLevel == LogLevel.SqlTrace) || (logLevel == LogLevel.Trace)) { + LOGGER.logWarning(SIG + "Debug - " + msg); + } + } + + public void logInfo(String msg) { + if (logLevel == LogLevel.NotSet) { + LOGGER.logInfo(SIG + msg); + } else if ((logLevel == LogLevel.Info) || (logLevel == LogLevel.Debug) || (logLevel == LogLevel.SqlTrace) + || (logLevel == LogLevel.Trace)) { + LOGGER.logWarning(SIG + "Info - " + msg); + } + } + + public void logWarn(String msg) { + LOGGER.logWarning(SIG + msg); + } + + public void logError(String msg) { + LOGGER.logError(SIG + msg); + } + + public void logError(String msg, Exception t) { + LOGGER.logError(SIG + msg, t); + } + + public static String getLoggingDetails() { + return "LogFileName=[" + LOGGER.getLogFileName() + "], LogName=[" + LOGGER.getLogName() + "], LogVerbosity=[" + + LOGGER.getVerbosityLevel().toExternalString() + "]"; + } +} diff --git a/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/BbUtil.java b/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/BbUtil.java index b2de088..8c52946 100644 --- a/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/BbUtil.java +++ b/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/BbUtil.java @@ -33,285 +33,246 @@ @SuppressWarnings("nls") // @NonNullByDefault public class BbUtil { - private static final LogService LOGGER = LogServiceFactory.getInstance(); - static { - System.out.println("BbUtil.LOGGER is " + LOGGER.getLogFileName()); - } - - public static final String HANDLE = "tle"; - public static final String VENDOR = "dych"; - public static final String CONTENT_HANDLER = "resource/tle-resource"; - public static final String CONTEXT_TAG = "@X@"; - public static final String CONTENT_ID = "content_id"; - public static final String CONTENT_ID_PLACEHOLDER = CONTEXT_TAG + "content.pk_string" + CONTEXT_TAG; - public static final String COURSE_ID = "course_id"; - public static final String COURSE_ID_PLACEHOLDER = CONTEXT_TAG + "course.pk_string" + CONTEXT_TAG; - - private static int majorVersionNumber; - private static int minorVersionNumber; - private static int revisionNumber; - private static String bbVersionString; - - private static final boolean trace = false; - private static final boolean sqlTrace = false; - - static { - bbVersionString = ConfigurationServiceFactory.getInstance().getBbProperty("bbconfig.version.number"); - final StringTokenizer st = new StringTokenizer(bbVersionString, "."); - if (st.hasMoreTokens()) { - majorVersionNumber = Integer.parseInt(st.nextToken()); - try { - minorVersionNumber = Integer.parseInt(st.nextToken()); - } catch (Exception e) { - // so be it - minorVersionNumber = -1; - } - - try { - revisionNumber = Integer.parseInt(st.nextToken()); - } catch (Exception e) { - // so be it - revisionNumber = -1; - } - } - } - - public static String getBbVersionString() { - return bbVersionString; - } - - /** - * Called from config.jsp - * - * @return - */ - public static int getMajorVersionNumber() { - return majorVersionNumber; - } - - /** - * Called from config.jsp - * - * @return - */ - public static int getMinorVersionNumber() { - return minorVersionNumber; - } - - public static int getRevisionNumber() { - return revisionNumber; - } - - private BbUtil() { - throw new Error(); - } - - public static String getParamBase() { - return new StringBuilder(CONTENT_ID).append("=").append(CONTENT_ID_PLACEHOLDER).append("&").append(COURSE_ID) - .append("=").append(COURSE_ID_PLACEHOLDER).toString(); - } - - public static String urlEncode(String url) { - try { - return URLEncoder.encode(url, "UTF-8"); - } catch (final UnsupportedEncodingException e) { - // Never happen - return url; - } - } - - public static String getBlockRelativePath() { - try { - return PlugInUtil.getUriStem(VENDOR, HANDLE); - } catch (final Exception t) { - error("Error getting relative path", t); - throw Throwables.propagate(t); - } - } - - public static Id getCourseId(String courseId) { - return getId(courseId, blackboard.data.course.Course.DATA_TYPE); - } - - public static Id getContentId(String contentId) { - return getId(contentId, blackboard.data.content.Content.DATA_TYPE); - } - - public static Id getFolderId(String folderId) { - return getId(folderId, blackboard.data.content.ContentFolder.DATA_TYPE); - } - - public static Id getUserId(String userId) { - return getId(userId, blackboard.data.user.User.DATA_TYPE); - } - - public static Id getId(String id, DataType type) { - try { - return BbContext.instance().getPersistenceManager().generateId(type, id); - } catch (PersistenceException e) { - AxisHelpers.throwWSException("EQ002", e.getMessage()); - // Should not reach here - throw Throwables.propagate(e); - } - } - - /* @Nullable */ - public static Content loadContent(ContentDbLoader contentDbLoader, String id) throws PersistenceException { - return loadContent(contentDbLoader, BbUtil.getContentId(id)); - } - - /* @Nullable */ - public static Content loadContent(ContentDbLoader contentDbLoader, Id id) throws PersistenceException { - try { - return contentDbLoader.loadById(id); - } - // KNF..uh huh uh huh uh huh. KNF's gunna rock ya - // http://www.youtube.com/watch?v=LXEOESuiYcA - catch (KeyNotFoundException knf) { - return null; - } - } - - /* @Nullable */ - public static Course loadCourse(CourseDbLoader courseDbLoader, String id) throws PersistenceException { - return loadCourse(courseDbLoader, BbUtil.getCourseId(id)); - } - - /* @Nullable */ - public static Course loadCourse(CourseDbLoader courseDbLoader, Id id) throws PersistenceException { - try { - return courseDbLoader.loadById(id); - } - // KNF..uh huh uh huh uh huh. KNF's gunna rock ya - // http://www.youtube.com/watch?v=LXEOESuiYcA - catch (KeyNotFoundException knf) { - return null; - } - } - - public static Iterable getBbFoldersForCourse(Id courseId) throws PersistenceException { - final BbPersistenceManager pm = BbContext.instance().getPersistenceManager(); - final CourseTocDbLoader courseTocDbLoader = (CourseTocDbLoader) pm.getLoader(CourseTocDbLoader.TYPE); - final ContentDbLoader contentDbLoader = (ContentDbLoader) pm.getLoader(ContentDbLoader.TYPE); - - // This is the Information / Contents etc list displayed on the - // left when viewing a course. - // But surely there is only one per course? - final BbList bbcourseTocs = courseTocDbLoader.loadByCourseId(courseId); - final List folders = new ArrayList(); - - for (CourseToc bbcourseToc : bbcourseTocs) { - // list the content - final Id contentId = bbcourseToc.getContentId(); - if (contentId.isSet()) { - final Content courseContent = contentDbLoader.loadById(contentId); - if (courseContent.getIsFolder()) { - folders.add(courseContent); - } - } - } - return folders; - } - - /** - * If you need subfolders only you need to check content.isFolder for each - * returned content. - * - * @param pm - * @param folderId - * @return - * @throws PersistenceException - */ - public static Iterable getBbContentForFolder(Id folderId) throws PersistenceException { - final ContentDbLoader contentDbLoader = (ContentDbLoader) BbContext.instance().getPersistenceManager() - .getLoader(ContentDbLoader.TYPE); - return contentDbLoader.loadChildren(folderId); - } - - public static void sqlTrace(/* @Nullable */Object text) { - // dev mode only - if (sqlTrace) { - LOGGER.logDebug(text == null ? "" : text.toString()); - System.out.println(text); - } - } - - public static void trace(/* @Nullable */Object text) { - // dev mode only - if (trace) { - LOGGER.logDebug(text == null ? "" : text.toString()); - System.out.println(text); - } - } - - public static void debug(String text) { - LOGGER.logDebug(text); - } - - public static void error(String text, /* @Nullable */Throwable t) { - if (t != null) { - LOGGER.logError(text, t); - System.out.println(text); - t.printStackTrace(System.out); - } else { - LOGGER.logError(text); - System.out.println(text); - } - } - - public static String ent(String szStr) { - if (Strings.isNullOrEmpty(szStr)) { - return ""; - } - - StringBuilder szOut = new StringBuilder(); - final char[] chars = szStr.toCharArray(); - for (final char ch : chars) { - switch (ch) { - case '<': - szOut.append("<"); - break; - - case '>': - szOut.append(">"); - break; - - case '&': - szOut.append("&"); - break; - - case '"': - szOut.append("""); - break; - - default: - // http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char - // regular displayable ASCII: - if (ch == 0xA || ch == 0xD || ch == 0x9 || (ch >= 0x20 && ch <= 0x007F)) { - szOut.append(ch); - } else if ((ch > 0x007F && ch <= 0xD7FF) || (ch >= 0xE000 && ch <= 0xFFFD) - || (ch >= 0x10000 && ch <= 0x10FFFF)) { - szOut.append("&#x"); - final String hexed = Integer.toHexString(ch); - // wooo, unrolled loops - switch (4 - hexed.length()) { - case 3: - case 2: - case 1: - szOut.append('0'); - break; - default: - break; - } - szOut.append(hexed); - szOut.append(';'); - } - // else we discard the character entirely. - // It CANNOT be placed in XML - break; - } - } - - return szOut.toString(); - } + public static final String HANDLE = "oeqPrimary"; + public static final String VENDOR = "aper"; + public static final String CONTENT_HANDLER = "resource/tle-resource"; + public static final String CONTEXT_TAG = "@X@"; + public static final String CONTENT_ID = "content_id"; + public static final String CONTENT_ID_PLACEHOLDER = CONTEXT_TAG + "content.pk_string" + CONTEXT_TAG; + public static final String COURSE_ID = "course_id"; + public static final String COURSE_ID_PLACEHOLDER = CONTEXT_TAG + "course.pk_string" + CONTEXT_TAG; + + private static int majorVersionNumber; + private static int minorVersionNumber; + private static int revisionNumber; + private static String bbVersionString; + + static { + bbVersionString = ConfigurationServiceFactory.getInstance().getBbProperty("bbconfig.version.number"); + final StringTokenizer st = new StringTokenizer(bbVersionString, "."); + if (st.hasMoreTokens()) { + majorVersionNumber = Integer.parseInt(st.nextToken()); + try { + minorVersionNumber = Integer.parseInt(st.nextToken()); + } catch (Exception e) { + // so be it + minorVersionNumber = -1; + } + + try { + revisionNumber = Integer.parseInt(st.nextToken()); + } catch (Exception e) { + // so be it + revisionNumber = -1; + } + } + } + + public static String getBbVersionString() { + return bbVersionString; + } + + /** + * Called from config.jsp + * + * @return + */ + public static int getMajorVersionNumber() { + return majorVersionNumber; + } + + /** + * Called from config.jsp + * + * @return + */ + public static int getMinorVersionNumber() { + return minorVersionNumber; + } + + public static int getRevisionNumber() { + return revisionNumber; + } + + private BbUtil() { + throw new Error(); + } + + public static String getParamBase() { + return new StringBuilder(CONTENT_ID).append("=").append(CONTENT_ID_PLACEHOLDER).append("&").append(COURSE_ID) + .append("=").append(COURSE_ID_PLACEHOLDER).toString(); + } + + public static String urlEncode(String url) { + try { + return URLEncoder.encode(url, "UTF-8"); + } catch (final UnsupportedEncodingException e) { + // Never happen + return url; + } + } + + public static String getBlockRelativePath() { + try { + return PlugInUtil.getUriStem(VENDOR, HANDLE); + } catch (final Exception t) { + BbLogger.instance().logError("Error getting relative path", t); + throw Throwables.propagate(t); + } + } + + public static Id getCourseId(String courseId) { + return getId(courseId, blackboard.data.course.Course.DATA_TYPE); + } + + public static Id getContentId(String contentId) { + return getId(contentId, blackboard.data.content.Content.DATA_TYPE); + } + + public static Id getFolderId(String folderId) { + return getId(folderId, blackboard.data.content.ContentFolder.DATA_TYPE); + } + + public static Id getUserId(String userId) { + return getId(userId, blackboard.data.user.User.DATA_TYPE); + } + + public static Id getId(String id, DataType type) { + try { + return BbContext.instance().getPersistenceManager().generateId(type, id); + } catch (PersistenceException e) { + AxisHelpers.throwWSException("EQ002", e.getMessage()); + // Should not reach here + throw Throwables.propagate(e); + } + } + + /* @Nullable */ + public static Content loadContent(ContentDbLoader contentDbLoader, String id) throws PersistenceException { + return loadContent(contentDbLoader, BbUtil.getContentId(id)); + } + + /* @Nullable */ + public static Content loadContent(ContentDbLoader contentDbLoader, Id id) throws PersistenceException { + try { + return contentDbLoader.loadById(id); + } + // KNF..uh huh uh huh uh huh. KNF's gunna rock ya + // http://www.youtube.com/watch?v=LXEOESuiYcA + catch (KeyNotFoundException knf) { + return null; + } + } + + /* @Nullable */ + public static Course loadCourse(CourseDbLoader courseDbLoader, String id) throws PersistenceException { + return loadCourse(courseDbLoader, BbUtil.getCourseId(id)); + } + + /* @Nullable */ + public static Course loadCourse(CourseDbLoader courseDbLoader, Id id) throws PersistenceException { + try { + return courseDbLoader.loadById(id); + } + // KNF..uh huh uh huh uh huh. KNF's gunna rock ya + // http://www.youtube.com/watch?v=LXEOESuiYcA + catch (KeyNotFoundException knf) { + return null; + } + } + + public static Iterable getBbFoldersForCourse(Id courseId) throws PersistenceException { + final BbPersistenceManager pm = BbContext.instance().getPersistenceManager(); + final CourseTocDbLoader courseTocDbLoader = (CourseTocDbLoader) pm.getLoader(CourseTocDbLoader.TYPE); + final ContentDbLoader contentDbLoader = (ContentDbLoader) pm.getLoader(ContentDbLoader.TYPE); + + // This is the Information / Contents etc list displayed on the + // left when viewing a course. + // But surely there is only one per course? + final BbList bbcourseTocs = courseTocDbLoader.loadByCourseId(courseId); + final List folders = new ArrayList(); + + for (CourseToc bbcourseToc : bbcourseTocs) { + // list the content + final Id contentId = bbcourseToc.getContentId(); + if (contentId.isSet()) { + final Content courseContent = contentDbLoader.loadById(contentId); + if (courseContent.getIsFolder()) { + folders.add(courseContent); + } + } + } + return folders; + } + + /** + * If you need subfolders only you need to check content.isFolder for each + * returned content. + * + * @param pm + * @param folderId + * @return + * @throws PersistenceException + */ + public static Iterable getBbContentForFolder(Id folderId) throws PersistenceException { + final ContentDbLoader contentDbLoader = (ContentDbLoader) BbContext.instance().getPersistenceManager() + .getLoader(ContentDbLoader.TYPE); + return contentDbLoader.loadChildren(folderId); + } + + public static String ent(String szStr) { + if (Strings.isNullOrEmpty(szStr)) { + return ""; + } + + StringBuilder szOut = new StringBuilder(); + final char[] chars = szStr.toCharArray(); + for (final char ch : chars) { + switch (ch) { + case '<': + szOut.append("<"); + break; + + case '>': + szOut.append(">"); + break; + + case '&': + szOut.append("&"); + break; + + case '"': + szOut.append("""); + break; + + default: + // http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char + // regular displayable ASCII: + if (ch == 0xA || ch == 0xD || ch == 0x9 || (ch >= 0x20 && ch <= 0x007F)) { + szOut.append(ch); + } else if ((ch > 0x007F && ch <= 0xD7FF) || (ch >= 0xE000 && ch <= 0xFFFD) + || (ch >= 0x10000 && ch <= 0x10FFFF)) { + szOut.append("&#x"); + final String hexed = Integer.toHexString(ch); + // wooo, unrolled loops + switch (4 - hexed.length()) { + case 3: + case 2: + case 1: + szOut.append('0'); + break; + default: + break; + } + szOut.append(hexed); + szOut.append(';'); + } + // else we discard the character entirely. + // It CANNOT be placed in XML + break; + } + } + + return szOut.toString(); + } } diff --git a/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/SqlUtil.java b/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/SqlUtil.java index ff7b42e..cdd40e0 100644 --- a/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/SqlUtil.java +++ b/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/SqlUtil.java @@ -17,177 +17,177 @@ @SuppressWarnings("nls") // @NonNullByDefault public class SqlUtil { - private static final String TABLE = "equellacontent"; - - public static StringBuilder select(String... columns) { - final StringBuilder sql = new StringBuilder("SELECT "); - if (columns.length == 0) { - sql.append("*"); - } else { - boolean first = true; - for (String col : columns) { - if (!first) { - sql.append(", "); - } - sql.append(col); - first = false; - } - } - return sql.append(" ").append(from()); - } - - public static StringBuilder update(String... columns) { - final StringBuilder sql = new StringBuilder("UPDATE ").append(table()).append("SET "); - boolean first = true; - for (String col : columns) { - if (!first) { - sql.append(", "); - } - sql.append(col); - sql.append("=?"); - first = false; - } - return sql.append(" "); - } - - public static StringBuilder insert(String... columns) { - StringBuilder sql = new StringBuilder("INSERT INTO ").append(table()).append("("); - boolean first = true; - for (String col : columns) { - if (!first) { - sql.append(", "); - } - sql.append(col); - first = false; - } - return sql.append(") "); - } - - public static boolean columnExists(String column) { - try { - boolean oracle = DbUtil.safeGetBbDatabase().isOracle(); - SqlUtil.runSql("SELECT " + (oracle ? "" : "TOP 1 ") + " " + column + " FROM " + table(oracle) - + (oracle ? " WHERE rownum < 1" : ""), null); - return true; - } catch (Exception e) { - // debug("Failed reading column "+ column +" from DB"); - return false; - } - } - - public static String delete() { - return "DELETE " + from(); - } - - public static String from() { - return "FROM " + table(); - } - - public static String table(boolean oracle) { - return getSchema(oracle) + TABLE + " "; - } - - public static String table() { - return getSchema() + TABLE + " "; - } - - public static String getSchema() { - return getSchema(DbUtil.safeGetBbDatabase().isOracle()); - } - - public static String getSchema(boolean oracle) { - return (oracle ? "" : "dbo."); - } - - public static List runSql(String sql, /* @Nullable */ResultProcessor processor, Object... params) { - PreparedStatement stmt = null; - List result = null; - - BbUtil.sqlTrace("Getting connection manager"); - ConnectionManager connMgr = DbUtil.safeGetBbDatabase().getConnectionManager(); - - Connection conn = null; - try { - BbUtil.sqlTrace("Getting connection"); - conn = connMgr.getConnection(); - - BbUtil.sqlTrace("Creating statement"); - stmt = conn.prepareStatement(sql); - - BbUtil.sqlTrace("Has " + params.length + " params"); - int index = 1; - for (Object param : params) { - if (param instanceof OptionalParam) { - final OptionalParam opt = (OptionalParam) param; - if (opt.isUsed()) { - setParam(stmt, index++, opt.getValue()); - } - } else { - setParam(stmt, index++, param); - } - } - - BbUtil.sqlTrace("Executing: " + sql); - if (processor != null) { - result = processor.getResults(stmt.executeQuery()); - } else { - stmt.execute(); - result = (List) Collections.singletonList(stmt.getUpdateCount()); - } - BbUtil.sqlTrace("Success!!"); - return result; - } catch (Exception e) { - BbUtil.error("Failed to runSql", e); - throw new RuntimeException(e); - } finally { - BbUtil.sqlTrace("Closing statement"); - DbUtil.closeStatement(stmt); - BbUtil.sqlTrace("Releasing connection"); - ConnectionManager.releaseDefaultConnection(conn); - } - } - - public static void setParam(PreparedStatement stmt, int index, /* @Nullable */Object param) throws SQLException { - if (param instanceof String) { - BbUtil.sqlTrace("Setting param string[" + index + "] = " + param); - stmt.setString(index, (String) param); - } else if (param instanceof Integer) { - BbUtil.sqlTrace("Setting param int[" + index + "] = " + param); - stmt.setInt(index, (Integer) param); - } else if (param instanceof Timestamp) { - BbUtil.sqlTrace("Setting param timestamp[" + index + "] = " + param); - stmt.setTimestamp(index, (Timestamp) param); - } else if (param instanceof Boolean) { - boolean pval = (Boolean) param; - BbUtil.sqlTrace("Setting param boolean[" + index + "] = " + pval); - stmt.setInt(index, pval ? 1 : 0); - } else if (param == null) { - BbUtil.sqlTrace("Setting param ?[" + index + "] = null"); - stmt.setString(index, null); - } else { - throw new RuntimeException("Parameter " + index + " is an unhandled type: " + param.getClass().getName()); - } - } - - public interface ResultProcessor { - List getResults(ResultSet results) throws SQLException; - } - - public static class OptionalParam { - private final T value; - private final boolean used; - - public OptionalParam(T value, boolean used) { - this.value = value; - this.used = used; - } - - public T getValue() { - return value; - } - - public boolean isUsed() { - return used; - } - } + private static final String TABLE = "equellacontent"; + + public static StringBuilder select(String... columns) { + final StringBuilder sql = new StringBuilder("SELECT "); + if (columns.length == 0) { + sql.append("*"); + } else { + boolean first = true; + for (String col : columns) { + if (!first) { + sql.append(", "); + } + sql.append(col); + first = false; + } + } + return sql.append(" ").append(from()); + } + + public static StringBuilder update(String... columns) { + final StringBuilder sql = new StringBuilder("UPDATE ").append(table()).append("SET "); + boolean first = true; + for (String col : columns) { + if (!first) { + sql.append(", "); + } + sql.append(col); + sql.append("=?"); + first = false; + } + return sql.append(" "); + } + + public static StringBuilder insert(String... columns) { + StringBuilder sql = new StringBuilder("INSERT INTO ").append(table()).append("("); + boolean first = true; + for (String col : columns) { + if (!first) { + sql.append(", "); + } + sql.append(col); + first = false; + } + return sql.append(") "); + } + + public static boolean columnExists(String column) { + try { + boolean oracle = DbUtil.safeGetBbDatabase().isOracle(); + SqlUtil.runSql("SELECT " + (oracle ? "" : "TOP 1 ") + " " + column + " FROM " + table(oracle) + + (oracle ? " WHERE rownum < 1" : ""), null); + return true; + } catch (Exception e) { + // debug("Failed reading column "+ column +" from DB"); + return false; + } + } + + public static String delete() { + return "DELETE " + from(); + } + + public static String from() { + return "FROM " + table(); + } + + public static String table(boolean oracle) { + return getSchema(oracle) + TABLE + " "; + } + + public static String table() { + return getSchema() + TABLE + " "; + } + + public static String getSchema() { + return getSchema(DbUtil.safeGetBbDatabase().isOracle()); + } + + public static String getSchema(boolean oracle) { + return (oracle ? "" : "dbo."); + } + + public static List runSql(String sql, /* @Nullable */ResultProcessor processor, Object... params) { + PreparedStatement stmt = null; + List result = null; + + BbLogger.instance().logSqlTrace("Getting connection manager"); + ConnectionManager connMgr = DbUtil.safeGetBbDatabase().getConnectionManager(); + + Connection conn = null; + try { + BbLogger.instance().logSqlTrace("Getting connection"); + conn = connMgr.getConnection(); + + BbLogger.instance().logSqlTrace("Creating statement"); + stmt = conn.prepareStatement(sql); + + BbLogger.instance().logSqlTrace("Has " + params.length + " params"); + int index = 1; + for (Object param : params) { + if (param instanceof OptionalParam) { + final OptionalParam opt = (OptionalParam) param; + if (opt.isUsed()) { + setParam(stmt, index++, opt.getValue()); + } + } else { + setParam(stmt, index++, param); + } + } + + BbLogger.instance().logSqlTrace("Executing: " + sql); + if (processor != null) { + result = processor.getResults(stmt.executeQuery()); + } else { + stmt.execute(); + result = (List) Collections.singletonList(stmt.getUpdateCount()); + } + BbLogger.instance().logSqlTrace("Success!!"); + return result; + } catch (Exception e) { + BbLogger.instance().logError("Failed to runSql", e); + throw new RuntimeException(e); + } finally { + BbLogger.instance().logSqlTrace("Closing statement"); + DbUtil.closeStatement(stmt); + BbLogger.instance().logSqlTrace("Releasing connection"); + ConnectionManager.releaseDefaultConnection(conn); + } + } + + public static void setParam(PreparedStatement stmt, int index, /* @Nullable */Object param) throws SQLException { + if (param instanceof String) { + BbLogger.instance().logSqlTrace("Setting param string[" + index + "] = " + param); + stmt.setString(index, (String) param); + } else if (param instanceof Integer) { + BbLogger.instance().logSqlTrace("Setting param int[" + index + "] = " + param); + stmt.setInt(index, (Integer) param); + } else if (param instanceof Timestamp) { + BbLogger.instance().logSqlTrace("Setting param timestamp[" + index + "] = " + param); + stmt.setTimestamp(index, (Timestamp) param); + } else if (param instanceof Boolean) { + boolean pval = (Boolean) param; + BbLogger.instance().logSqlTrace("Setting param boolean[" + index + "] = " + pval); + stmt.setInt(index, pval ? 1 : 0); + } else if (param == null) { + BbLogger.instance().logSqlTrace("Setting param ?[" + index + "] = null"); + stmt.setString(index, null); + } else { + throw new RuntimeException("Parameter " + index + " is an unhandled type: " + param.getClass().getName()); + } + } + + public interface ResultProcessor { + List getResults(ResultSet results) throws SQLException; + } + + public static class OptionalParam { + private final T value; + private final boolean used; + + public OptionalParam(T value, boolean used) { + this.value = value; + this.used = used; + } + + public T getValue() { + return value; + } + + public boolean isUsed() { + return used; + } + } } diff --git a/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/content/ContentUtil.java b/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/content/ContentUtil.java index f2cfb0b..8cf7036 100644 --- a/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/content/ContentUtil.java +++ b/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/content/ContentUtil.java @@ -26,6 +26,7 @@ import com.google.common.base.Strings; import com.google.common.base.Throwables; import org.apereo.openequella.integration.blackboard.common.BbContext; +import org.apereo.openequella.integration.blackboard.common.BbLogger; import org.apereo.openequella.integration.blackboard.common.BbUtil; import org.apereo.openequella.integration.blackboard.common.PathUtils; @@ -34,375 +35,375 @@ */ @SuppressWarnings("nls") public class ContentUtil { - private static ContentUtil instance; - private static final Object instanceLock = new Object(); - - // TODO: privatise - public static final String FIELD_DESCRIPTION = "description"; - public static final String FIELD_UUID = "uuid"; - public static final String FIELD_VERSION = "version"; - /** - * This is a *relative* URL. I.e. it doesn't include the institution portion. - */ - public static final String FIELD_URL = "url"; - public static final String FIELD_ATTACHMENT_NAME = "attachmentName"; - public static final String FIELD_ATTACHMENT_UUID = "attachmentUuid"; - public static final String FIELD_MIME_TYPE = "mimeType"; - public static final String FIELD_ACTIVATIONUUID = "activationUuid"; - - private static final List GRADABLE_MIMES = Arrays.asList("equella/attachment-lti", "equella/qtitest", - "equella/scorm-package"); - - private ContentDbLoader contentLoader; - private ContentDbPersister contentPersister; - - public static ContentUtil instance() { - if (instance == null) { - synchronized (instanceLock) { - if (instance == null) { - instance = new ContentUtil(); - } - } - } - return instance; - } - - private ContentUtil() { - } - - /** - * @param course - * @param folderId - * @param uuid - * @param version - * @param url - * Relative URL - * @param title - * @param description - * @param attachmentUuid - * @param attachmentName - * @param mimeType - * @param equellaUrl - * @return - */ - public Content addContent(Course course, Id folderId, String uuid, int version, String url, String title, - String description, String attachmentUuid, String attachmentName, String mimeType, String equellaUrl, - boolean newWindow) { - try { - final Content content = new CourseDocument(); - content.setIsFolder(false); - content.setTitle(title); - content.setCourseId(course.getId()); - content.setParentId(folderId); - content.setIsAvailable(true); - content.setLaunchInNewWindow(true); - content.setContentHandler(BbUtil.CONTENT_HANDLER); - content.setPosition(-1); - content.setIsSequential(false); - content.setLaunchInNewWindow(newWindow); - - ExtendedData extendedData = content.getExtendedData(); - if (extendedData == null) { - extendedData = new ExtendedData(); - content.setExtendedData(extendedData); - } - extendedData.setValue(FIELD_UUID, uuid); - extendedData.setValue(FIELD_VERSION, Integer.toString(version)); - extendedData.setValue(FIELD_URL, url); - if (description != null) { - extendedData.setValue(FIELD_DESCRIPTION, description); - } - if (attachmentUuid != null) { - extendedData.setValue(FIELD_ATTACHMENT_UUID, attachmentUuid); - } - if (attachmentName != null) { - extendedData.setValue(FIELD_ATTACHMENT_NAME, attachmentName); - } - if (mimeType != null) { - extendedData.setValue(FIELD_MIME_TYPE, mimeType); - } - - return persistContent(content, course, equellaUrl, true); - } catch (Exception e) { - BbUtil.error("Error saving new content", e); - throw Throwables.propagate(e); - } - } - - public Content persistContent(Content content, Course course, String equellaUrl, boolean isNew) { - try { - ItemInfo itemInfo = ItemUtil.getItemInfo(content, course, content.getParentId(), equellaUrl); - final ItemKey itemKey = itemInfo.getItemKey(); - - final FormattedText text = new FormattedText( - ItemUtil.getHtml(equellaUrl, itemKey.getPage(), itemInfo.getAttachmentName(), itemInfo.getMimeType(), - content.getTitle(), itemInfo.getDescription(), content.getLaunchInNewWindow()), - FormattedText.Type.HTML); - content.setBody(text); - - BbUtil.trace("persisting content for url " + itemKey.getPage()); - getContentPersister().persist(content); - BbUtil.trace("content success!"); - - // need to do this again so the contentId doesn't get munged up - itemInfo = ItemUtil.getItemInfo(content, course, content.getParentId(), equellaUrl); - register(content, course, itemInfo, equellaUrl, isNew); - ensureGradableItem(content, isNew, true); - - return content; - } catch (Exception e) { - BbUtil.error("Error persisting content", e); - throw Throwables.propagate(e); - } - } - - public Content loadContent(Id contentId) { - try { - return getContentLoader().loadById(contentId); - } catch (Exception e) { - BbUtil.error("Error loading content", e); - throw Throwables.propagate(e); - } - } - - public Content loadContentForViewing(Id contentId) { - try { - final Content content = getContentLoader().loadById(contentId); - RegistrationUtil.updateDateAccessed(contentId.toExternalString(), new Date()); - ensureGradableItem(content, false, false); - return content; - } catch (Exception e) { - BbUtil.error("Error loading content for viewing", e); - throw Throwables.propagate(e); - } - } - - private void ensureGradableItem(Content content, boolean isNewContent, boolean modifyExisting) throws Exception { - BbUtil.trace("ensureGradableItem"); - - final String mimeType = content.getExtendedData().getValue(FIELD_MIME_TYPE); - final BasicLTIPlacement ltiPlacement = PlacementUtil.getDefaultPlacement(); - if (ltiPlacement == null || !ltiPlacement.isAllowGrading() || !GRADABLE_MIMES.contains(mimeType)) { - return; - } - - final GradableItemManager gradableItemManager = GradebookManagerFactory - .getGradableItemManagerWithoutSecurityCheck(); - boolean foundExisting = false; - final GradableItem gradableItem; - if (isNewContent) { - gradableItem = createGradableItem(content); - } else { - GradableItem gitem = gradableItemManager.getGradebookItemByContentId(content.getId()); - if (gitem == null) { - gitem = createGradableItem(content); - } else { - BbUtil.trace("gradable item already exists"); - foundExisting = true; - } - gradableItem = gitem; - } - if (modifyExisting || !foundExisting) { - gradableItem.setTitle(content.getTitle()); - gradableItem.setCourseId(content.getCourseId()); - gradableItem.setDateModified(Calendar.getInstance()); - try { - gradableItemManager.persistGradebookItem(gradableItem); - } catch (BbSecurityException e) { - BbUtil.error("Error saving gradableItem", e); - throw Throwables.propagate(e); - } - BbUtil.trace("gradable item success!"); - } else { - BbUtil.trace("not modifying existing gradable item"); - } - } - - @SuppressWarnings("deprecation") - private void register(Content content, Course course, ItemInfo itemInfo, String equellaUrl, boolean isNew) { - try { - // Register in EQUELLA table - BbUtil.trace("Registering EQUELLA content in database"); - - final ItemKey itemKey = itemInfo.getItemKey(); - - BbUtil.debug("Registering content " + itemKey + " which is " + (isNew ? "new" : "NOT new")); - - final Date createdDate = (content.getCreatedDate() == null ? new Date() : content.getCreatedDate().getTime()); - final Date modifiedDate = (content.getModifiedDate() == null ? createdDate : content.getModifiedDate().getTime()); - - RegistrationUtil.recordItem(itemKey, isNew, true, course.getIsAvailable(), content.getTitle(), - itemInfo.getDescription(), itemInfo.getAttachmentName(), createdDate, modifiedDate, course.getTitle()); - } catch (Exception t) { - BbUtil.error("An error occurred while registering content", t); - } - } - - private GradableItem createGradableItem(Content content) { - final GradableItem gradableItem = new GradableItem(); - gradableItem.setCourseContentId(content.getId()); - gradableItem.setScoreProviderHandle(BbUtil.CONTENT_HANDLER); - gradableItem.setVisibleInBook(true); - gradableItem.setVisibleToStudents(true); - gradableItem.setPoints(100); - gradableItem.setHideAttempt(false); - gradableItem.setDateAdded(Calendar.getInstance()); - gradableItem.setScorable(true); - return gradableItem; - } - - public void removeContent(Content content, Course course, String equellaUrl, String token) { - try { - final ItemInfo itemInfo = ItemUtil.getItemInfo(content, course, content.getParentId(), equellaUrl); - - unregister(itemInfo.getItemKey()); - deactivate(itemInfo, equellaUrl, token); - removeGradableItem(content); - - getContentPersister().deleteById(content.getId()); - } catch (Exception e) { - BbUtil.error("Error removing content", e); - throw Throwables.propagate(e); - } - } - - private void removeGradableItem(Content content) { - try { - // Delete gradebook item - final GradableItemManager gradableItemManager = GradebookManagerFactory - .getGradableItemManagerWithoutSecurityCheck(); - final GradableItem gradableItem = gradableItemManager.getGradebookItemByContentId(content.getId()); - if (gradableItem != null) { - try { - gradableItemManager.deleteGradebookItem(gradableItem.getId()); - } catch (BbSecurityException e) { - BbUtil.error("Error removing gradableItem", e); - throw Throwables.propagate(e); - } - } - } catch (Exception e) { - BbUtil.error("Error removing GradableItem", e); - throw Throwables.propagate(e); - } - } - - private void unregister(ItemKey itemKey) { - try { - final ContentRegisteredResponse reg = RegistrationUtil.contentIsRegistered(itemKey); - if (reg.isRegistered()) { - BbUtil.debug("Unregistering content " + itemKey + ""); - RegistrationUtil.unrecordItem(reg.getId(), reg.getContentId()); - } else { - BbUtil.debug("Content " + itemKey + " is not registered"); - } - } catch (Exception t) { - BbUtil.error("An error occurred while un-registering content", t); - } - } - - private void deactivate(ItemInfo itemInfo, String equellaUrl, String token) { - GetMethod method = null; - try { - final String requestUuid = itemInfo.getActivateRequestUuid(); - if (!Strings.isNullOrEmpty(requestUuid)) { - // FIXME: No option but tokens.... (for now) - final String url = PathUtils.urlPath(equellaUrl, "access/activationwebservice.do") + "?token=" - + BbUtil.urlEncode(token) + "&activationUuid=" + BbUtil.urlEncode(requestUuid); - method = new GetMethod(url); - - final HttpClient client = new HttpClient(); - client.getHttpConnectionManager().getParams().setConnectionTimeout(10000); - client.executeMethod(method.getHostConfiguration(), method, client.getState()); - final String response = method.getResponseBodyAsString(); - if (!response.equals("OK")) { - throw new Exception("Error deactivating attachment: " + response); - } - } - } catch (final Exception e) { - BbUtil.error("An error occurred while deactivating content", e); - throw Throwables.propagate(e); - } finally { - if (method != null) { - method.releaseConnection(); - } - } - } - - public boolean isLegacy(Content content) { - final FormattedText body = content.getBody(); - if (body != null && body.getFormattedText().startsWith("" - + "" - + "
\"
" - + "" - + "
{1}
\"
" - + "
\"*\"  {6}
"; - - // @formatter:on - - public static String getHtml(String serverUrl, String xmlString) { - PropBagMin xml = new PropBagMin(xmlString); - - String attTitle = xml.getNode("attachments/@selectedTitle"); - String selectedPage = xml.getNode("attachments/@selected"); - String selectedAttachmentType = xml.getNode("attachments/@selectedType"); - String description = xml.getNode("description"); - String title = xml.getNode("name"); - String url = selectedPage; - - String fileForIcon = selectedPage; - if ("link".equals(selectedAttachmentType)) { - fileForIcon = "http://"; - } - - if (serverUrl.endsWith("/")) { - serverUrl = serverUrl.substring(0, serverUrl.length() - 1); - } - - final String html = MessageFormat.format(HTML_TEMPLATE, serverUrl, BbUtil.ent(description), - BbUtil.getBlockRelativePath(), BbUtil.ent(title), url, xml, BbUtil.ent(attTitle), - ItemUtil.getIcon(fileForIcon)); - return html; - } - - public static ItemInfo getItemInfo(Content content, Course course, Id folderId, String equellaUrl) { - String body = extractXmlFromBody(content.getBody().getFormattedText()); - final PropBagMin propBag = createPropBag(body); - return parseXml(propBag, content.getId().toExternalString(), course.getId().toExternalString(), - folderId.toExternalString(), equellaUrl); - } - - // Legacy items can have XML that is not always clean. This method covers - // one method of scrubbing the XML and allows future additions as clients - // need them. - protected static PropBagMin createPropBag(String body) { - try { - // Try with the original legacy XML - return new PropBagMin(body); - } catch (final Exception e) { - // That failed. Escape the '&' in the XML and try again - try { - String ampEscapedBody = fixUnescapedAmpersands(body); - return new PropBagMin(ampEscapedBody); - } catch (final Exception e2) { - // Possible future enhancement to escape entities such as   - // or &blahblah; - for now, fail with an error. - LOGGER.logError("Error parsing body of item \n" + body, e2); - throw new RuntimeException(e2); - } - } - } - - // Due to EQ-2260. When &'s are in the item body xml, Equella 6.2+ building - // block fails. Maybe not 100% effective, but should handle the vast - // majority of cases - protected static String fixUnescapedAmpersands(String original) { - final String regex = "&([^;\\W]*([^;\\w]|$))"; - final String replacement = "&$1"; - // The double replaceAll is to handle cases where there are multiple & - // together. - return original.replaceAll(regex, replacement).replaceAll(regex, replacement); - } - - // Due to EQ-2261. Legacy pages are not always ready to be directly called - // and need to be cleaned up - public static String scrubLegacyPage(String original) { - // Due to EQAS-403 / EQ-2306 - if ((original == null) || original.equals("./")) { - return ""; - } - - // When requesting pages that have folder structures in the item, - // don't scrub the path (it should be already clean) - int filenameIndex = original.lastIndexOf("/"); - if (filenameIndex != -1) { - return original; - } - - // There is no folder structure. Scrub the path. Note - Equella doesn't - // seem to like the escape character for space (+) - switch to %20 - return urlEncode(original).replaceAll("\\+", "%20"); - } - - public static String extractXmlFromBody(String body) { - if (body.length() == 0 || !body.startsWith(""); - return body.substring(4, temp); - } - - public static String extractUrlFromBody(String body) { - // This is grotesque. Maybe this should also be recorded in the usage - // table? - final Matcher matcher = PATTERN_URL.matcher(body); - matcher.matches(); - return matcher.group(1); - } - - private static ItemInfo parseXml(PropBagMin xml, String contentId, String courseId, String folderId, - String equellaUrl) { - if (xml.nodeExists("result")) { - xml = xml.getSubtree("result"); - } - if (xml.nodeExists("item") && !xml.getNodeName().equals("item")) { - xml = xml.getSubtree("item"); - } - - final ItemInfo itemInfo = new ItemInfo(equellaUrl, xml.getNode("@id"), xml.getIntNode("@version", 1), contentId, - courseId, folderId, xml.getNode("attachments/@selected")); - - itemInfo.setName(xml.getNode("name")); - itemInfo.setDescription(xml.getNode("description")); - itemInfo.setActivateRequestUuid(xml.getNode("requestUuid", null)); - final String selectedAttachmentType = xml.getNode("attachments/@selectedType"); - if (selectedAttachmentType.equals("link")) { - itemInfo.setMimeType("equella/link"); - } else { - itemInfo.setMimeType("application/octet-stream"); - } - - return itemInfo; - } - - public static String urlEncode(String url) { - try { - return URLEncoder.encode(url, "UTF-8"); - } catch (final UnsupportedEncodingException e) { - // Never happen - return url; - } - } + private static final LogService LOGGER = LogServiceFactory.getInstance(); + + private static final Pattern PATTERN_URL = Pattern.compile(".*&page=([^\"|^&]*).*", Pattern.DOTALL); + + // @formatter:off + // {0} = Equella url + // {1} = Description + // {2} = Blackboard relative url + // {3} = Title + // {4} = Item url + // {5} = Item xml + // {6} = Attachment name + // {7} = Icon url + public static final String HTML_TEMPLATE = "" + + "" + + "
\"
" + + "" + + "
{1}
\"
" + + "
\"*\"  {6}
"; + + // @formatter:on + + public static String getHtml(String serverUrl, String xmlString) { + PropBagMin xml = new PropBagMin(xmlString); + + String attTitle = xml.getNode("attachments/@selectedTitle"); + String selectedPage = xml.getNode("attachments/@selected"); + String selectedAttachmentType = xml.getNode("attachments/@selectedType"); + String description = xml.getNode("description"); + String title = xml.getNode("name"); + String url = selectedPage; + + String fileForIcon = selectedPage; + if ("link".equals(selectedAttachmentType)) { + fileForIcon = "http://"; + } + + if (serverUrl.endsWith("/")) { + serverUrl = serverUrl.substring(0, serverUrl.length() - 1); + } + + final String html = MessageFormat.format(HTML_TEMPLATE, serverUrl, BbUtil.ent(description), + BbUtil.getBlockRelativePath(), BbUtil.ent(title), url, xml, BbUtil.ent(attTitle), + ItemUtil.getIcon(fileForIcon)); + return html; + } + + public static ItemInfo getItemInfo(Content content, Course course, Id folderId, String equellaUrl) { + String body = extractXmlFromBody(content.getBody().getFormattedText()); + final PropBagMin propBag = createPropBag(body); + return parseXml(propBag, content.getId().toExternalString(), course.getId().toExternalString(), + folderId.toExternalString(), equellaUrl); + } + + // Legacy items can have XML that is not always clean. This method covers + // one method of scrubbing the XML and allows future additions as clients + // need them. + protected static PropBagMin createPropBag(String body) { + try { + // Try with the original legacy XML + return new PropBagMin(body); + } catch (final Exception e) { + // That failed. Escape the '&' in the XML and try again + try { + String ampEscapedBody = fixUnescapedAmpersands(body); + return new PropBagMin(ampEscapedBody); + } catch (final Exception e2) { + // Possible future enhancement to escape entities such as   + // or &blahblah; - for now, fail with an error. + LOGGER.logError("Error parsing body of item \n" + body, e2); + throw new RuntimeException(e2); + } + } + } + + // Due to EQ-2260. When &'s are in the item body xml, Equella 6.2+ building + // block fails. Maybe not 100% effective, but should handle the vast + // majority of cases + protected static String fixUnescapedAmpersands(String original) { + final String regex = "&([^;\\W]*([^;\\w]|$))"; + final String replacement = "&$1"; + // The double replaceAll is to handle cases where there are multiple & + // together. + return original.replaceAll(regex, replacement).replaceAll(regex, replacement); + } + + // Due to EQ-2261. Legacy pages are not always ready to be directly called + // and need to be cleaned up + public static String scrubLegacyPage(String original) { + // Due to EQAS-403 / EQ-2306 + if ((original == null) || original.equals("./")) { + return ""; + } + + // When requesting pages that have folder structures in the item, + // don't scrub the path (it should be already clean) + int filenameIndex = original.lastIndexOf("/"); + if (filenameIndex != -1) { + return original; + } + + // There is no folder structure. Scrub the path. Note - Equella doesn't + // seem to like the escape character for space (+) - switch to %20 + return urlEncode(original).replaceAll("\\+", "%20"); + } + + public static String extractXmlFromBody(String body) { + if (body.length() == 0 || !body.startsWith(""); + return body.substring(4, temp); + } + + public static String extractUrlFromBody(String body) { + // This is grotesque. Maybe this should also be recorded in the usage + // table? + final Matcher matcher = PATTERN_URL.matcher(body); + matcher.matches(); + return matcher.group(1); + } + + private static ItemInfo parseXml(PropBagMin xml, String contentId, String courseId, String folderId, + String equellaUrl) { + if (xml.nodeExists("result")) { + xml = xml.getSubtree("result"); + } + if (xml.nodeExists("item") && !xml.getNodeName().equals("item")) { + xml = xml.getSubtree("item"); + } + + final ItemInfo itemInfo = new ItemInfo(equellaUrl, xml.getNode("@id"), xml.getIntNode("@version", 1), contentId, + courseId, folderId, xml.getNode("attachments/@selected")); + + itemInfo.setName(xml.getNode("name")); + itemInfo.setDescription(xml.getNode("description")); + itemInfo.setActivateRequestUuid(xml.getNode("requestUuid", null)); + final String selectedAttachmentType = xml.getNode("attachments/@selectedType"); + if (selectedAttachmentType.equals("link")) { + itemInfo.setMimeType("equella/link"); + } else { + itemInfo.setMimeType("application/octet-stream"); + } + + return itemInfo; + } + + public static String urlEncode(String url) { + try { + return URLEncoder.encode(url, "UTF-8"); + } catch (final UnsupportedEncodingException e) { + // Never happen + return url; + } + } } \ No newline at end of file diff --git a/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/content/PlacementUtil.java b/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/content/PlacementUtil.java index 59dbf64..52df8cb 100644 --- a/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/content/PlacementUtil.java +++ b/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/content/PlacementUtil.java @@ -19,6 +19,8 @@ import blackboard.platform.plugin.ContentHandlerType.ActionType; import com.google.common.base.Throwables; + +import org.apereo.openequella.integration.blackboard.common.BbLogger; import org.apereo.openequella.integration.blackboard.common.BbUtil; import org.apereo.openequella.integration.blackboard.common.SqlUtil; import org.apereo.openequella.integration.blackboard.common.SqlUtil.ResultProcessor; @@ -29,136 +31,137 @@ @SuppressWarnings("nls") // @NonNullByDefault public abstract class PlacementUtil { - private static final String LTI_PLACEMENT_TITLE = "EQUELLA LTI Integration"; - - public PlacementUtil() { - throw new Error(); - } - - public static BasicLTIPlacement getDefaultPlacement() { - return PlacementUtil.loadFromHandle("resource/tle-resource"); - } - - /* @Nullable */ - public static BasicLTIPlacement loadFromHandle(String handle) { - final String sql = "SELECT pk1 FROM blti_placement WHERE handle=?"; - final List ids = SqlUtil.runSql(sql.toString(), new ResultProcessor() { - @Override - public List getResults(ResultSet results) throws SQLException { - final List idList = new ArrayList(); - while (results.next()) { - final Long contentId = results.getLong("pk1"); - final Id id = Id.toId(BasicLTIPlacement.DATA_TYPE, Long.toString(contentId)); - idList.add(id); - } - return idList; - } - }, handle); - if (ids.size() == 0) { - BbUtil.trace("No placements found for handle " + handle); - return null; - } - BbUtil.trace("Placements found for handle " + handle); - return loadFromId(ids.get(0)); - } - - /* @Nullable */ - public static BasicLTIPlacement loadFromId(Id placementId) { - final BasicLTIPlacementManager basicLTIPlacementManager = BasicLTIPlacementManager.Factory.getInstance(); - try { - return basicLTIPlacementManager.loadById(placementId, false); - } catch (KeyNotFoundException e) { - return null; - } - } - - public static LoadPlacementResponse loadPlacementByUrl(String urlString) throws Exception { - final LoadPlacementResponse response = new LoadPlacementResponse(); - // Handle badly formatted old URLs - final URL url; - try { - url = new URL(urlString); - } catch (MalformedURLException mal) { - return response; - } - - final BasicLTIDomainConfig domainConfig = loadDomainConfigByUrl(url); - response.setDomainConfig(domainConfig); - if (domainConfig != null) { - BbUtil.trace("Existing BasicLTIDomainConfig, loading placements"); - final BasicLTIPlacementManager basicLTIPlacementManager = BasicLTIPlacementManager.Factory.getInstance(); - final List placements = basicLTIPlacementManager.loadByDomainConfigId(domainConfig.getId()); - if (placements == null || placements.size() == 0) { - BbUtil.trace("No existing BasicLTIPlacements for domain config"); - } else { - // what if more than one? - BbUtil.trace("Found " + placements.size() + " existing BasicLTIPlacements for host " + url.getHost()); - response.setPlacement(placements.get(0)); - } - } else { - BbUtil.trace("No domain config for this URL"); - } - return response; - } - - /* @Nullable */ - public static BasicLTIDomainConfig loadDomainConfigByUrl(URL url) { - final BasicLTIDomainConfigManager fac = BasicLTIDomainConfigManagerFactory.getInstance(); - try { - return fac.loadByDomain(url.getHost()); - } catch (KeyNotFoundException knfUhHuhUhHuh) { - return null; - } - } - - public static BasicLTIPlacement createNewPlacement(BasicLTIDomainConfig domainConfig, ContentHandler contentHandler) { - BbUtil.trace("Creating new BasicLTIPlacement"); - try { - final BasicLTIPlacement placement = new BasicLTIPlacement(); - placement.setType(Type.ContentHandler); - placement.setAllowGrading(true); - placement.setContentHandler(contentHandler); - placement.setName(LTI_PLACEMENT_TITLE); - placement.setHandle(contentHandler.getHandle()); - placement.setBasicLTIDomainConfigId(domainConfig.getId()); - return placement; - } catch (Exception e) { - BbUtil.error("Error creating placement", e); - throw Throwables.propagate(e); - } - } - - public static void deleteById(Id id) { - BbUtil.trace("Deleting placement " + id.toExternalString()); - final BasicLTIPlacementManager basicLTIPlacementManager = BasicLTIPlacementManager.Factory.getInstance(); - basicLTIPlacementManager.deleteById(id); - } - - public static void save(BasicLTIPlacement placement) { - BbUtil.trace("Saving placement " + placement.getId().toExternalString()); - final BasicLTIPlacementManager basicLTIPlacementManager = BasicLTIPlacementManager.Factory.getInstance(); - basicLTIPlacementManager.saveContentHandlerPlacement(placement, ActionType.none); - } - - // @NonNullByDefault(false) - public static class LoadPlacementResponse { - private BasicLTIPlacement placement; - private BasicLTIDomainConfig domainConfig; - - public BasicLTIPlacement getPlacement() { - return placement; - } - - public void setPlacement(BasicLTIPlacement placement) { - this.placement = placement; - } - - public BasicLTIDomainConfig getDomainConfig() { - return domainConfig; - } - - public void setDomainConfig(BasicLTIDomainConfig domainConfig) { - this.domainConfig = domainConfig; - } - } + private static final String LTI_PLACEMENT_TITLE = "openEQUELLA LTI Integration"; + + public PlacementUtil() { + throw new Error(); + } + + public static BasicLTIPlacement getDefaultPlacement() { + return PlacementUtil.loadFromHandle("resource/tle-resource"); + } + + /* @Nullable */ + public static BasicLTIPlacement loadFromHandle(String handle) { + final String sql = "SELECT pk1 FROM blti_placement WHERE handle=?"; + final List ids = SqlUtil.runSql(sql.toString(), new ResultProcessor() { + @Override + public List getResults(ResultSet results) throws SQLException { + final List idList = new ArrayList(); + while (results.next()) { + final Long contentId = results.getLong("pk1"); + final Id id = Id.toId(BasicLTIPlacement.DATA_TYPE, Long.toString(contentId)); + idList.add(id); + } + return idList; + } + }, handle); + if (ids.size() == 0) { + BbLogger.instance().logTrace("No placements found for handle " + handle); + return null; + } + BbLogger.instance().logTrace("Placements found for handle " + handle); + return loadFromId(ids.get(0)); + } + + /* @Nullable */ + public static BasicLTIPlacement loadFromId(Id placementId) { + final BasicLTIPlacementManager basicLTIPlacementManager = BasicLTIPlacementManager.Factory.getInstance(); + try { + return basicLTIPlacementManager.loadById(placementId, false); + } catch (KeyNotFoundException e) { + return null; + } + } + + public static LoadPlacementResponse loadPlacementByUrl(String urlString) throws Exception { + final LoadPlacementResponse response = new LoadPlacementResponse(); + // Handle badly formatted old URLs + final URL url; + try { + url = new URL(urlString); + } catch (MalformedURLException mal) { + return response; + } + + final BasicLTIDomainConfig domainConfig = loadDomainConfigByUrl(url); + response.setDomainConfig(domainConfig); + if (domainConfig != null) { + BbLogger.instance().logTrace("Existing BasicLTIDomainConfig, loading placements"); + final BasicLTIPlacementManager basicLTIPlacementManager = BasicLTIPlacementManager.Factory.getInstance(); + final List placements = basicLTIPlacementManager.loadByDomainConfigId(domainConfig.getId()); + if (placements == null || placements.size() == 0) { + BbLogger.instance().logTrace("No existing BasicLTIPlacements for domain config"); + } else { + // what if more than one? + BbLogger.instance() + .logTrace("Found " + placements.size() + " existing BasicLTIPlacements for host " + url.getHost()); + response.setPlacement(placements.get(0)); + } + } else { + BbLogger.instance().logTrace("No domain config for this URL"); + } + return response; + } + + /* @Nullable */ + public static BasicLTIDomainConfig loadDomainConfigByUrl(URL url) { + final BasicLTIDomainConfigManager fac = BasicLTIDomainConfigManagerFactory.getInstance(); + try { + return fac.loadByDomain(url.getHost()); + } catch (KeyNotFoundException knfUhHuhUhHuh) { + return null; + } + } + + public static BasicLTIPlacement createNewPlacement(BasicLTIDomainConfig domainConfig, ContentHandler contentHandler) { + BbLogger.instance().logTrace("Creating new BasicLTIPlacement"); + try { + final BasicLTIPlacement placement = new BasicLTIPlacement(); + placement.setType(Type.ContentHandler); + placement.setAllowGrading(true); + placement.setContentHandler(contentHandler); + placement.setName(LTI_PLACEMENT_TITLE); + placement.setHandle(contentHandler.getHandle()); + placement.setBasicLTIDomainConfigId(domainConfig.getId()); + return placement; + } catch (Exception e) { + BbLogger.instance().logError("Error creating placement", e); + throw Throwables.propagate(e); + } + } + + public static void deleteById(Id id) { + BbLogger.instance().logTrace("Deleting placement " + id.toExternalString()); + final BasicLTIPlacementManager basicLTIPlacementManager = BasicLTIPlacementManager.Factory.getInstance(); + basicLTIPlacementManager.deleteById(id); + } + + public static void save(BasicLTIPlacement placement) { + BbLogger.instance().logTrace("Saving placement " + placement.getId().toExternalString()); + final BasicLTIPlacementManager basicLTIPlacementManager = BasicLTIPlacementManager.Factory.getInstance(); + basicLTIPlacementManager.saveContentHandlerPlacement(placement, ActionType.none); + } + + // @NonNullByDefault(false) + public static class LoadPlacementResponse { + private BasicLTIPlacement placement; + private BasicLTIDomainConfig domainConfig; + + public BasicLTIPlacement getPlacement() { + return placement; + } + + public void setPlacement(BasicLTIPlacement placement) { + this.placement = placement; + } + + public BasicLTIDomainConfig getDomainConfig() { + return domainConfig; + } + + public void setDomainConfig(BasicLTIDomainConfig domainConfig) { + this.domainConfig = domainConfig; + } + } } diff --git a/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/content/RegistrationUtil.java b/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/content/RegistrationUtil.java index 173ba49..fa20069 100644 --- a/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/content/RegistrationUtil.java +++ b/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/content/RegistrationUtil.java @@ -17,6 +17,8 @@ import blackboard.platform.persistence.PersistenceServiceFactory; import com.google.common.base.Strings; + +import org.apereo.openequella.integration.blackboard.common.BbLogger; import org.apereo.openequella.integration.blackboard.common.BbUtil; import org.apereo.openequella.integration.blackboard.common.SqlUtil; import org.apereo.openequella.integration.blackboard.common.SqlUtil.OptionalParam; @@ -29,523 +31,525 @@ @SuppressWarnings("nls") // @NonNullByDefault public abstract class RegistrationUtil { - // until proven otherwise. The databaseVersion is currently 5 (as of after 6.3 - // GA) - private static int databaseVersion = 0; - - private static final String COL_DATABASE_ID = "ID"; - private static final String COL_INSTITUTION_URL = "INSTURL"; - private static final String COL_DATE_CREATED = "DTCREATED"; - private static final String COL_DATE_MODIFIED = "DTMODIFIED"; - private static final String COL_UUID = "UUID"; - private static final String COL_VERSION = "VERSION"; - private static final String COL_PAGE = "PAGE"; - private static final String COL_CONTENT_ID = "CONTENTID"; - private static final String COL_BLACKBOARD_TITLE = "BBTITLE"; - private static final String COL_BLACKBOARD_DESC = "BBDESC"; - private static final String COL_COURSE_NAME = "COURSENAME"; - private static final String COL_COURSE_ID = "COURSEID"; - private static final String COL_FOLDER_ID = "FOLDERID"; - private static final String COL_AVAILABLE = "AVAILABLE"; - private static final String COL_COURSE_AVAILABLE = "COURSEAVAILABLE"; - private static final String COL_DATE_ACCESSED = "DTACCESSED"; - private static final String COL_ATTACHMENT_NAME = "ATTNAME"; - - public RegistrationUtil() { - throw new Error(); - } - - public static void recordItem(ItemKey itemKey, boolean added, boolean available, boolean courseAvailable, - String bbTitle, String bbDesc, String attachmentName, Date dateAdded, Date dateModifed, String courseName) { - BbUtil.trace("recordItem(" + itemKey + ", " + added + ", " + available + ", " + courseAvailable + ", " + bbTitle - + ", " + bbDesc + ", " + attachmentName + ", " + dateAdded + ", " + dateModifed + ", " + courseName + ")"); - ensureDatabase(); - - StringBuilder sql = null; - - final boolean oracle = getDatabase().isOracle(); - final String schema = SqlUtil.getSchema(oracle); - - final long now = System.currentTimeMillis(); - final java.sql.Timestamp addedTimestamp = new java.sql.Timestamp(dateAdded == null ? now : dateAdded.getTime()); - final java.sql.Timestamp modifiedTimestamp = new java.sql.Timestamp( - dateModifed == null ? now : dateModifed.getTime()); - - if (added) { - if (oracle) { - sql = insert(COL_DATABASE_ID, COL_DATE_CREATED, COL_DATE_MODIFIED, COL_INSTITUTION_URL, COL_UUID, COL_VERSION, - COL_COURSE_ID, COL_FOLDER_ID, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, - COL_BLACKBOARD_DESC, COL_PAGE, COL_CONTENT_ID, COL_COURSE_NAME, COL_ATTACHMENT_NAME).append(" VALUES (") - .append(schema).append("equellaseq.nextval, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ,?, ?, ?, ?)"); - } else { - sql = insert(COL_DATE_CREATED, COL_DATE_MODIFIED, COL_INSTITUTION_URL, COL_UUID, COL_VERSION, COL_COURSE_ID, - COL_FOLDER_ID, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC, COL_PAGE, - COL_CONTENT_ID, COL_COURSE_NAME, COL_ATTACHMENT_NAME) - .append(" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); - } - SqlUtil.runSql(sql.toString(), null, addedTimestamp, modifiedTimestamp, itemKey.getInstitutionUrl(), - itemKey.getUuid(), itemKey.getVersion(), itemKey.getCourseId(), itemKey.getFolderId(), available, - courseAvailable, bbTitle, trim(bbDesc, 511), itemKey.getPage(), itemKey.getContentId(), courseName, - attachmentName); - itemKey.setDatabaseId(0); // would be nice to know what the new one - // is - } else { - String contentId = itemKey.getContentId(); - if (!Strings.isNullOrEmpty(contentId)) { - BbUtil.debug("updating recorded usage using contentId"); - - sql = update(COL_DATE_MODIFIED, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC, - COL_PAGE, COL_COURSE_NAME, COL_ATTACHMENT_NAME).append("WHERE CONTENTID=?"); - List updateCount = SqlUtil.runSql(sql.toString(), null, modifiedTimestamp, available, courseAvailable, - bbTitle, bbDesc, itemKey.getPage(), courseName, attachmentName, contentId); - - int updates = updateCount.get(0); - if (updates > 1) { - // There have instances of dupes. Remove them all. This - // should be a rare occurrence - BbUtil.error("** duplicate recordings of " + contentId + " **", null); - - // remove dupes and add a brand new entry - unrecordItem(0, contentId); - recordItem(itemKey, true, available, courseAvailable, bbTitle, bbDesc, attachmentName, dateAdded, dateModifed, - courseName); - } else if (updates == 0) { - BbUtil.debug("content ID not logged... updating the existing one and record the content ID"); - - sql = update(COL_DATE_MODIFIED, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, - COL_BLACKBOARD_DESC, COL_PAGE, COL_COURSE_NAME, COL_CONTENT_ID, COL_ATTACHMENT_NAME) - .append("WHERE INSTURL=? AND UUID=? AND VERSION=? AND COURSEID=? AND FOLDERID=?"); - updateCount = SqlUtil.runSql(sql.toString(), null, modifiedTimestamp, available, courseAvailable, bbTitle, - trim(bbDesc, 511), itemKey.getPage(), courseName, itemKey.getContentId(), attachmentName, - itemKey.getInstitutionUrl(), itemKey.getUuid(), itemKey.getVersion(), itemKey.getCourseId(), - itemKey.getFolderId()); - checkSingleUpdate(updateCount); - } - } else { - BbUtil.debug("updating recorded usage using old field matching"); - - sql = update(COL_DATE_MODIFIED, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC, - COL_PAGE, COL_COURSE_NAME, COL_ATTACHMENT_NAME) - .append("WHERE INSTURL=? AND UUID=? AND VERSION=? AND COURSEID=? AND FOLDERID=?"); - final List updateCount = SqlUtil.runSql(sql.toString(), null, modifiedTimestamp, available, - courseAvailable, bbTitle, trim(bbDesc, 511), itemKey.getPage(), courseName, attachmentName, - itemKey.getInstitutionUrl(), itemKey.getUuid(), itemKey.getVersion(), itemKey.getCourseId(), - itemKey.getFolderId()); - checkSingleUpdate(updateCount); - } - } - } - - private static boolean checkSingleUpdate(List updateCount) { - final int updates = updateCount.get(0); - if (updates != 1) { - // houston, we have a problem - BbUtil.error("** Updated " + updates + " rows when only one should have been updated **", null); - return false; - } - return true; - } - - private static boolean unrecordItem(int usageId) { - BbUtil.debug("unrecordItem(" + usageId + ")"); - ensureDatabase(); - - final List updateCount = SqlUtil.runSql(delete() + "WHERE ID=?", null, usageId); - return updateCount.get(0) > 0; - } - - public static boolean unrecordItem(int usageId, String contentId) { - BbUtil.debug("unrecordItem(" + usageId + ", " + contentId + ")"); - ensureDatabase(); - - if (usageId != 0) { - boolean unrecorded = unrecordItem(usageId); - if (unrecorded) { - return true; - } - } - - final List updateCount = SqlUtil.runSql(delete() + "WHERE CONTENTID=?", null, contentId); - return updateCount.get(0) > 0; - } - - public static boolean updateRecordedTitle(String contentId, String title, String description) { - BbUtil.debug("updateRecordedTitle(" + contentId + ", " + title + ", " + description + ")"); - ensureDatabase(); - - final List updateCount = SqlUtil.runSql( - update(COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC).append("WHERE CONTENTID=?").toString(), null, title, - description, contentId); - return updateCount.get(0) > 0; - } - - public static boolean updateRecordedLocation(String contentId, String courseId, String folderId) { - BbUtil.debug("updateRecordedLocation(" + contentId + ", " + courseId + ", " + folderId + ")"); - ensureDatabase(); - - final List updateCount = SqlUtil.runSql( - update(COL_COURSE_ID, COL_FOLDER_ID).append("WHERE CONTENTID=?").toString(), null, courseId, folderId, - contentId); - return updateCount.get(0) > 0; - } - - public static boolean updateDateAccessed(String contentId, Date dateAccessed) { - BbUtil.debug("updateDateAccessed(" + contentId + ", " + dateAccessed + ")"); - ensureDatabase(); - - final Timestamp accessedTimestamp = new Timestamp(dateAccessed.getTime()); - final List updateCount = SqlUtil.runSql(update(COL_DATE_ACCESSED).append("WHERE CONTENTID=?").toString(), - null, accessedTimestamp, contentId); - return updateCount.get(0) > 0; - } - - public static List findUsages(final String institutionUrl, final String itemUuid, final int itemVersion, - boolean versionIsLatest, boolean allVersions, boolean available) { - BbUtil.debug("findUsages(" + institutionUrl + ", " + itemUuid + ", " + itemVersion + ", " + allVersions + ", " - + available + ")"); - ensureDatabase(); - - final StringBuilder sql = select().append("WHERE INSTURL=? AND UUID=?"); - if (!allVersions) { - sql.append(" AND (VERSION=?"); - if (versionIsLatest) { - sql.append(" OR VERSION=0"); - } - sql.append(")"); - } - if (available) { - sql.append(" AND AVAILABLE=? AND COURSEAVAILABLE=?"); - } - sql.append(" ORDER BY DTCREATED DESC"); - - final List usages = SqlUtil.runSql(sql.toString(), new ResultProcessor() { - @Override - public List getResults(ResultSet results) throws SQLException { - final List u = new ArrayList(); - while (results.next()) { - final String contentId = results.getString(COL_CONTENT_ID); - final ItemInfo r = new ItemInfo(institutionUrl, itemUuid, results.getInt(COL_VERSION), contentId, - results.getString(COL_COURSE_ID), results.getString(COL_FOLDER_ID), results.getString(COL_PAGE)); - r.setCreatedDate(results.getTimestamp(COL_DATE_CREATED)); - r.setModifiedDate(results.getTimestamp(COL_DATE_MODIFIED)); - r.setAvailable(results.getBoolean(COL_AVAILABLE)); - r.setName(results.getString(COL_BLACKBOARD_TITLE)); - r.setDescription(results.getString(COL_BLACKBOARD_DESC)); - r.getItemKey().setDatabaseId(results.getInt(COL_DATABASE_ID)); - r.setAttachmentName(results.getString(COL_ATTACHMENT_NAME)); - u.add(r); - } - return u; - } - }, institutionUrl, itemUuid, new OptionalParam(itemVersion, !allVersions), - new OptionalParam(available, available), new OptionalParam(available, available)); - return usages; - } - - public static List findAllUsages(final String institutionUrl, final String query, final String courseId, - String folderId, boolean available, String sortColumn, boolean sortReverse) { - BbUtil.debug("findAllUsages(" + institutionUrl + ", " + query + ", " + courseId + ", " + folderId + ", " + available - + ", " + sortColumn + ", " + sortReverse + ")"); - ensureDatabase(); - - final StringBuilder sql = select().append(" WHERE INSTURL=?"); - - final boolean hasQuery = !Strings.isNullOrEmpty(query); - String likeQuery = null; - if (hasQuery) { - likeQuery = '%' + query.toLowerCase() + '%'; - sql.append(" AND LOWER(BBTITLE) LIKE ?"); - } - final boolean hasCourseId = !Strings.isNullOrEmpty(courseId); - if (hasCourseId) { - sql.append(" AND COURSEID = ?"); - } - final boolean hasFolderId = !Strings.isNullOrEmpty(folderId); - if (hasFolderId) { - sql.append(" AND FOLDERID = ?"); - } - if (available) { - sql.append(" AND AVAILABLE=? AND COURSEAVAILABLE=?"); - } - - String sortCol = "DTCREATED"; - String sortOrd = sortReverse ? "DESC" : "ASC"; - if (!Strings.isNullOrEmpty(sortColumn)) { - if (sortColumn.equals("name")) { - sortCol = "LOWER(BBTITLE)"; - } else if (sortColumn.equals("course")) { - sortCol = "LOWER(COURSENAME)"; - } - } - sql.append(" ORDER BY " + sortCol + " " + sortOrd); - - // @formatter:off - final List usages = SqlUtil.runSql(sql.toString(), new ResultProcessor() { - @Override - public List getResults(ResultSet results) throws SQLException { - final List u = new ArrayList(); - while (results.next()) { - final ItemInfo r = new ItemInfo(institutionUrl, results.getString(COL_UUID), results.getInt(COL_VERSION), - results.getString(COL_CONTENT_ID), results.getString(COL_COURSE_ID), results.getString(COL_FOLDER_ID), - results.getString(COL_PAGE)); - r.setCreatedDate(results.getTimestamp(COL_DATE_CREATED)); - r.setModifiedDate(results.getTimestamp(COL_DATE_MODIFIED)); - r.setAvailable(results.getBoolean(COL_AVAILABLE)); - r.setName(results.getString(COL_BLACKBOARD_TITLE)); - r.setDescription(results.getString(COL_BLACKBOARD_DESC)); - r.getItemKey().setDatabaseId(results.getInt(COL_DATABASE_ID)); - r.setDateAccessed(results.getTimestamp(COL_DATE_ACCESSED)); - r.setAttachmentName(results.getString(COL_ATTACHMENT_NAME)); - u.add(r); - } - return u; - } - }, institutionUrl, new OptionalParam(likeQuery, hasQuery), new OptionalParam(courseId, hasCourseId), - new OptionalParam(folderId, hasFolderId), new OptionalParam(available, available), - new OptionalParam(available, available)); - // @formatter:on - return usages; - } - - // Used by SynchroniseContentThread - public static List findEquellaContentByCourse(final String institutionUrl, final String courseId, - boolean available) { - BbUtil.debug("findEquellaContentByCourse(" + institutionUrl + ", " + courseId + ", " + available + ")"); - ensureDatabase(); - - // ID, UUID, VERSION, FOLDERID, ATTACHMENT_NAME, CONTENTID - final StringBuilder sql = select(COL_DATABASE_ID, COL_UUID, COL_VERSION, COL_FOLDER_ID, COL_PAGE, COL_CONTENT_ID) - .append("WHERE INSTURL=? AND COURSEID=?"); - if (available) { - sql.append(" AND AVAILABLE=? AND COURSEAVAILABLE=?"); - } - - final List usages = SqlUtil.runSql(sql.toString(), new ResultProcessor() { - @Override - public List getResults(ResultSet results) throws SQLException { - List u = new ArrayList(); - while (results.next()) { - final int dbId = results.getInt(COL_DATABASE_ID); - final String uuid = results.getString(COL_UUID); - final int version = results.getInt(COL_VERSION); - final String folderId = results.getString(COL_FOLDER_ID); - final String page = results.getString(COL_PAGE); - final String contentId = results.getString(COL_CONTENT_ID); - - final ItemKey k = new ItemKey(institutionUrl, uuid, version, contentId, courseId, folderId, page); - k.setDatabaseId(dbId); - u.add(k); - } - return u; - } - }, institutionUrl, courseId, new OptionalParam(available, available), - new OptionalParam(available, available)); - return usages; - } - - public static int cleanupBadContent(final String institutionUrl) { - BbUtil.debug("cleanupBadContent(" + institutionUrl + ")"); - ensureDatabase(); - - String sql = delete() + "WHERE INSTURL=? AND CONTENTID IS NULL"; - List updates = SqlUtil.runSql(sql, null, institutionUrl); - return updates.get(0); - } - - public static ContentRegisteredResponse contentIsRegistered(ItemKey itemKey) { - BbUtil.debug("contentIsRegistered(" + itemKey + ")"); - ensureDatabase(); - - final ResultProcessor resultProcessor = new ResultProcessor() { - @Override - public List getResults(ResultSet results) throws SQLException { - List r = new ArrayList(); - while (results.next()) { - r.add(new Object[] { results.getInt(COL_DATABASE_ID), results.getBoolean(COL_AVAILABLE), - results.getString(COL_CONTENT_ID) }); - } - return r; - } - }; - - final List res; - final StringBuilder sql = select(COL_DATABASE_ID, COL_AVAILABLE, COL_CONTENT_ID); - if (itemKey.getContentId() != null) { - sql.append("WHERE CONTENTID=?"); - res = SqlUtil.runSql(sql.toString(), resultProcessor, itemKey.getContentId()); - } else { - // OLD STYLE - final String page = itemKey.getPage(); - sql.append("WHERE INSTURL=? AND UUID=? AND VERSION=? AND COURSEID=? AND FOLDERID=? AND PAGE" - + (Strings.isNullOrEmpty(page) ? " IS NULL" : "=?")); - - res = SqlUtil.runSql(sql.toString(), resultProcessor, itemKey.getInstitutionUrl(), itemKey.getUuid(), - itemKey.getVersion(), itemKey.getCourseId(), itemKey.getFolderId(), - new OptionalParam(page, !Strings.isNullOrEmpty(page))); - } - - final ContentRegisteredResponse response = new ContentRegisteredResponse(); - if (!res.isEmpty()) { - response.setRegistered(true); - final Object[] r = res.get(0); - response.setId((Integer) r[0]); - response.setAvailable((Boolean) r[1]); - response.setContentId((String) r[2]); - } - - return response; - } - - private static void ensureDatabase() { - if (databaseVersion < 1) { - try { - SqlUtil.runSql(SqlUtil.select("1").toString(), null); - databaseVersion = 1; - } catch (Exception e) { - makeDatabase(); - databaseVersion = 1; - } - } - - if (databaseVersion < 2) { - if (!SqlUtil.columnExists(COL_CONTENT_ID)) { - modifyDatabaseAddContentAndCourseName(); - } - databaseVersion = 2; - } - - if (databaseVersion < 3) { - if (!SqlUtil.columnExists(COL_BLACKBOARD_DESC)) { - modifyDatabaseAddDescription(); - } - databaseVersion = 3; - } - - if (databaseVersion < 4) { - if (!SqlUtil.columnExists(COL_DATE_ACCESSED)) { - modifyDatabaseAddDateAccessed(); - } - databaseVersion = 4; - } - - if (databaseVersion < 5) { - if (!SqlUtil.columnExists(COL_ATTACHMENT_NAME)) { - modifyDatabaseAddAttachmentName(); - } - databaseVersion = 5; - } - } - - /** - * Todo: this clearly isn't normalised. But the question is, is it worth it? - */ - private static void makeDatabase() { - final String sql; - if (getDatabase().isOracle()) { - BbUtil.debug("Oracle DB detected"); - - SqlUtil.runSql("CREATE SEQUENCE equellaseq START WITH 1 INCREMENT BY 1 NOMAXVALUE", null); - - // @formatter:off - sql = "CREATE TABLE equellacontent (" + "ID NUMBER NOT NULL, " + "INSTURL NVARCHAR2(255) NOT NULL, " - + "DTCREATED DATE DEFAULT SYSDATE, " + "DTMODIFIED DATE DEFAULT SYSDATE, " - + "BBTITLE NVARCHAR2(1024) NOT NULL," + "PAGE NVARCHAR2(1024)," // nullable for item summaries - + "UUID NVARCHAR2(40) NOT NULL, " + "VERSION NUMBER NOT NULL," + "COURSEID NVARCHAR2(32) NOT NULL, " - + "FOLDERID NVARCHAR2(32) NOT NULL, " + "AVAILABLE NUMBER NOT NULL," + "COURSEAVAILABLE NUMBER NOT NULL," - + "CONSTRAINT equellacontent_pk PRIMARY KEY (ID))"; - // @formatter:on - SqlUtil.runSql(sql, null); - - SqlUtil.runSql("CREATE INDEX eqcontent_uuidversion ON equellacontent(INSTURL, UUID, VERSION)", null); - // cannot be UNIQUE since same item could be added to same folder - // (in theory) - SqlUtil.runSql("CREATE INDEX eqcontent_all ON equellacontent(INSTURL, UUID, VERSION, COURSEID, FOLDERID)", null); - SqlUtil.runSql("CREATE INDEX eqcontent_course ON equellacontent(INSTURL, COURSEID)", null); - SqlUtil.runSql("CREATE INDEX eqcontent_available ON equellacontent(AVAILABLE, COURSEAVAILABLE)", null); - } else { - BbUtil.debug("SQL Server DB detected"); - // @formatter:off - sql = "CREATE TABLE dbo.equellacontent (" + "ID int NOT NULL PRIMARY KEY IDENTITY, " - + "INSTURL nvarchar(255) NOT NULL, " + "BBTITLE nvarchar(1024) NOT NULL, " + "PAGE nvarchar(1024) NULL, " // nullable - // for - // item - // summaries - + "DTCREATED datetime NOT NULL, " + "DTMODIFIED datetime NOT NULL, " + "UUID nvarchar(40) NOT NULL, " - + "VERSION int NOT NULL," + "COURSEID nvarchar(32) NOT NULL, " + "FOLDERID nvarchar(32) NOT NULL, " - + "AVAILABLE bit NOT NULL," + "COURSEAVAILABLE bit NOT NULL" + ")"; - // @formatter:on - SqlUtil.runSql(sql, null); - - SqlUtil.runSql("CREATE INDEX eqcontent_course ON dbo.equellacontent(INSTURL, COURSEID)", null); - // cannot be UNIQUE since same item could be added to same folder - // (in theory) - SqlUtil.runSql("CREATE INDEX eqcontent_all ON dbo.equellacontent(INSTURL, UUID, VERSION, COURSEID, FOLDERID)", - null); - SqlUtil.runSql("CREATE INDEX eqcontent_uuidversion ON dbo.equellacontent(INSTURL, UUID, VERSION)", null); - SqlUtil.runSql("CREATE INDEX eqcontent_available ON dbo.equellacontent(AVAILABLE, COURSEAVAILABLE)", null); - } - } - - private static void modifyDatabaseAddContentAndCourseName() { - if (getDatabase().isOracle()) { - // debug("Oracle DB detected"); - // @formatter:off - final String sql = "ALTER TABLE equellacontent ADD (" + "CONTENTID NVARCHAR2(32) NULL, " - + "COURSENAME NVARCHAR2(512) NULL" + ")"; - // @formatter:on - SqlUtil.runSql(sql, null); - SqlUtil.runSql("CREATE INDEX eqcontent_content ON equellacontent(INSTURL, CONTENTID)", null); - } else { - // debug("SQL Server DB detected"); - // @formatter:off - final String sql = "ALTER TABLE dbo.equellacontent ADD " + "CONTENTID nvarchar(32) NULL, " - + "COURSENAME nvarchar(512) NULL"; - // @formatter:on - SqlUtil.runSql(sql, null); - SqlUtil.runSql("CREATE INDEX eqcontent_content ON dbo.equellacontent(INSTURL, CONTENTID)", null); - } - } - - private static void modifyDatabaseAddDescription() { - final String sql; - if (getDatabase().isOracle()) { - sql = "ALTER TABLE equellacontent ADD (BBDESC NVARCHAR2(512) NULL)"; - } else { - sql = "ALTER TABLE dbo.equellacontent ADD BBDESC nvarchar(512) NULL"; - } - SqlUtil.runSql(sql, null); - } - - private static void modifyDatabaseAddDateAccessed() { - final String sql; - if (getDatabase().isOracle()) { - sql = "ALTER TABLE equellacontent ADD (" + COL_DATE_ACCESSED + " DATE NULL)"; - } else { - sql = "ALTER TABLE dbo.equellacontent ADD " + COL_DATE_ACCESSED + " datetime NULL"; - } - SqlUtil.runSql(sql, null); - } - - private static void modifyDatabaseAddAttachmentName() { - final String sql; - if (getDatabase().isOracle()) { - sql = "ALTER TABLE equellacontent ADD (" + COL_ATTACHMENT_NAME + " NVARCHAR2(512) NULL)"; - } else { - sql = "ALTER TABLE dbo.equellacontent ADD " + COL_ATTACHMENT_NAME + " nvarchar(512) NULL"; - } - SqlUtil.runSql(sql, null); - } - - private static BbDatabase getDatabase() { - final DatabaseContainer dbContainer = (DatabaseContainer) PersistenceServiceFactory.getInstance() - .getDbPersistenceManager().getContainer(); - return dbContainer.getBbDatabase(); - } - - private static String trim(String text, int maxlen) { - if (text != null && text.length() > maxlen) { - return text.substring(0, maxlen); - } - return text; - } + // until proven otherwise. The databaseVersion is currently 5 (as of after 6.3 + // GA) + private static int databaseVersion = 0; + + private static final String COL_DATABASE_ID = "ID"; + private static final String COL_INSTITUTION_URL = "INSTURL"; + private static final String COL_DATE_CREATED = "DTCREATED"; + private static final String COL_DATE_MODIFIED = "DTMODIFIED"; + private static final String COL_UUID = "UUID"; + private static final String COL_VERSION = "VERSION"; + private static final String COL_PAGE = "PAGE"; + private static final String COL_CONTENT_ID = "CONTENTID"; + private static final String COL_BLACKBOARD_TITLE = "BBTITLE"; + private static final String COL_BLACKBOARD_DESC = "BBDESC"; + private static final String COL_COURSE_NAME = "COURSENAME"; + private static final String COL_COURSE_ID = "COURSEID"; + private static final String COL_FOLDER_ID = "FOLDERID"; + private static final String COL_AVAILABLE = "AVAILABLE"; + private static final String COL_COURSE_AVAILABLE = "COURSEAVAILABLE"; + private static final String COL_DATE_ACCESSED = "DTACCESSED"; + private static final String COL_ATTACHMENT_NAME = "ATTNAME"; + + public RegistrationUtil() { + throw new Error(); + } + + public static void recordItem(ItemKey itemKey, boolean added, boolean available, boolean courseAvailable, + String bbTitle, String bbDesc, String attachmentName, Date dateAdded, Date dateModifed, String courseName) { + BbLogger.instance() + .logTrace("recordItem(" + itemKey + ", " + added + ", " + available + ", " + courseAvailable + ", " + bbTitle + + ", " + bbDesc + ", " + attachmentName + ", " + dateAdded + ", " + dateModifed + ", " + courseName + ")"); + ensureDatabase(); + + StringBuilder sql = null; + + final boolean oracle = getDatabase().isOracle(); + final String schema = SqlUtil.getSchema(oracle); + + final long now = System.currentTimeMillis(); + final java.sql.Timestamp addedTimestamp = new java.sql.Timestamp(dateAdded == null ? now : dateAdded.getTime()); + final java.sql.Timestamp modifiedTimestamp = new java.sql.Timestamp( + dateModifed == null ? now : dateModifed.getTime()); + + if (added) { + if (oracle) { + sql = insert(COL_DATABASE_ID, COL_DATE_CREATED, COL_DATE_MODIFIED, COL_INSTITUTION_URL, COL_UUID, COL_VERSION, + COL_COURSE_ID, COL_FOLDER_ID, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, + COL_BLACKBOARD_DESC, COL_PAGE, COL_CONTENT_ID, COL_COURSE_NAME, COL_ATTACHMENT_NAME).append(" VALUES (") + .append(schema).append("equellaseq.nextval, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ,?, ?, ?, ?)"); + } else { + sql = insert(COL_DATE_CREATED, COL_DATE_MODIFIED, COL_INSTITUTION_URL, COL_UUID, COL_VERSION, COL_COURSE_ID, + COL_FOLDER_ID, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC, COL_PAGE, + COL_CONTENT_ID, COL_COURSE_NAME, COL_ATTACHMENT_NAME) + .append(" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + } + SqlUtil.runSql(sql.toString(), null, addedTimestamp, modifiedTimestamp, itemKey.getInstitutionUrl(), + itemKey.getUuid(), itemKey.getVersion(), itemKey.getCourseId(), itemKey.getFolderId(), available, + courseAvailable, bbTitle, trim(bbDesc, 511), itemKey.getPage(), itemKey.getContentId(), courseName, + attachmentName); + itemKey.setDatabaseId(0); // would be nice to know what the new one + // is + } else { + String contentId = itemKey.getContentId(); + if (!Strings.isNullOrEmpty(contentId)) { + BbLogger.instance().logDebug("updating recorded usage using contentId"); + + sql = update(COL_DATE_MODIFIED, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC, + COL_PAGE, COL_COURSE_NAME, COL_ATTACHMENT_NAME).append("WHERE CONTENTID=?"); + List updateCount = SqlUtil.runSql(sql.toString(), null, modifiedTimestamp, available, courseAvailable, + bbTitle, bbDesc, itemKey.getPage(), courseName, attachmentName, contentId); + + int updates = updateCount.get(0); + if (updates > 1) { + // There have instances of dupes. Remove them all. This + // should be a rare occurrence + BbLogger.instance().logError("** duplicate recordings of " + contentId + " **", null); + + // remove dupes and add a brand new entry + unrecordItem(0, contentId); + recordItem(itemKey, true, available, courseAvailable, bbTitle, bbDesc, attachmentName, dateAdded, dateModifed, + courseName); + } else if (updates == 0) { + BbLogger.instance().logDebug("content ID not logged... updating the existing one and record the content ID"); + + sql = update(COL_DATE_MODIFIED, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, + COL_BLACKBOARD_DESC, COL_PAGE, COL_COURSE_NAME, COL_CONTENT_ID, COL_ATTACHMENT_NAME) + .append("WHERE INSTURL=? AND UUID=? AND VERSION=? AND COURSEID=? AND FOLDERID=?"); + updateCount = SqlUtil.runSql(sql.toString(), null, modifiedTimestamp, available, courseAvailable, bbTitle, + trim(bbDesc, 511), itemKey.getPage(), courseName, itemKey.getContentId(), attachmentName, + itemKey.getInstitutionUrl(), itemKey.getUuid(), itemKey.getVersion(), itemKey.getCourseId(), + itemKey.getFolderId()); + checkSingleUpdate(updateCount); + } + } else { + BbLogger.instance().logDebug("updating recorded usage using old field matching"); + + sql = update(COL_DATE_MODIFIED, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC, + COL_PAGE, COL_COURSE_NAME, COL_ATTACHMENT_NAME) + .append("WHERE INSTURL=? AND UUID=? AND VERSION=? AND COURSEID=? AND FOLDERID=?"); + final List updateCount = SqlUtil.runSql(sql.toString(), null, modifiedTimestamp, available, + courseAvailable, bbTitle, trim(bbDesc, 511), itemKey.getPage(), courseName, attachmentName, + itemKey.getInstitutionUrl(), itemKey.getUuid(), itemKey.getVersion(), itemKey.getCourseId(), + itemKey.getFolderId()); + checkSingleUpdate(updateCount); + } + } + } + + private static boolean checkSingleUpdate(List updateCount) { + final int updates = updateCount.get(0); + if (updates != 1) { + // houston, we have a problem + BbLogger.instance().logError("** Updated " + updates + " rows when only one should have been updated **", null); + return false; + } + return true; + } + + private static boolean unrecordItem(int usageId) { + BbLogger.instance().logDebug("unrecordItem(" + usageId + ")"); + ensureDatabase(); + + final List updateCount = SqlUtil.runSql(delete() + "WHERE ID=?", null, usageId); + return updateCount.get(0) > 0; + } + + public static boolean unrecordItem(int usageId, String contentId) { + BbLogger.instance().logDebug("unrecordItem(" + usageId + ", " + contentId + ")"); + ensureDatabase(); + + if (usageId != 0) { + boolean unrecorded = unrecordItem(usageId); + if (unrecorded) { + return true; + } + } + + final List updateCount = SqlUtil.runSql(delete() + "WHERE CONTENTID=?", null, contentId); + return updateCount.get(0) > 0; + } + + public static boolean updateRecordedTitle(String contentId, String title, String description) { + BbLogger.instance().logDebug("updateRecordedTitle(" + contentId + ", " + title + ", " + description + ")"); + ensureDatabase(); + + final List updateCount = SqlUtil.runSql( + update(COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC).append("WHERE CONTENTID=?").toString(), null, title, + description, contentId); + return updateCount.get(0) > 0; + } + + public static boolean updateRecordedLocation(String contentId, String courseId, String folderId) { + BbLogger.instance().logDebug("updateRecordedLocation(" + contentId + ", " + courseId + ", " + folderId + ")"); + ensureDatabase(); + + final List updateCount = SqlUtil.runSql( + update(COL_COURSE_ID, COL_FOLDER_ID).append("WHERE CONTENTID=?").toString(), null, courseId, folderId, + contentId); + return updateCount.get(0) > 0; + } + + public static boolean updateDateAccessed(String contentId, Date dateAccessed) { + BbLogger.instance().logDebug("updateDateAccessed(" + contentId + ", " + dateAccessed + ")"); + ensureDatabase(); + + final Timestamp accessedTimestamp = new Timestamp(dateAccessed.getTime()); + final List updateCount = SqlUtil.runSql(update(COL_DATE_ACCESSED).append("WHERE CONTENTID=?").toString(), + null, accessedTimestamp, contentId); + return updateCount.get(0) > 0; + } + + public static List findUsages(final String institutionUrl, final String itemUuid, final int itemVersion, + boolean versionIsLatest, boolean allVersions, boolean available) { + BbLogger.instance().logDebug("findUsages(" + institutionUrl + ", " + itemUuid + ", " + itemVersion + ", " + + allVersions + ", " + available + ")"); + ensureDatabase(); + + final StringBuilder sql = select().append("WHERE INSTURL=? AND UUID=?"); + if (!allVersions) { + sql.append(" AND (VERSION=?"); + if (versionIsLatest) { + sql.append(" OR VERSION=0"); + } + sql.append(")"); + } + if (available) { + sql.append(" AND AVAILABLE=? AND COURSEAVAILABLE=?"); + } + sql.append(" ORDER BY DTCREATED DESC"); + + final List usages = SqlUtil.runSql(sql.toString(), new ResultProcessor() { + @Override + public List getResults(ResultSet results) throws SQLException { + final List u = new ArrayList(); + while (results.next()) { + final String contentId = results.getString(COL_CONTENT_ID); + final ItemInfo r = new ItemInfo(institutionUrl, itemUuid, results.getInt(COL_VERSION), contentId, + results.getString(COL_COURSE_ID), results.getString(COL_FOLDER_ID), results.getString(COL_PAGE)); + r.setCreatedDate(results.getTimestamp(COL_DATE_CREATED)); + r.setModifiedDate(results.getTimestamp(COL_DATE_MODIFIED)); + r.setAvailable(results.getBoolean(COL_AVAILABLE)); + r.setName(results.getString(COL_BLACKBOARD_TITLE)); + r.setDescription(results.getString(COL_BLACKBOARD_DESC)); + r.getItemKey().setDatabaseId(results.getInt(COL_DATABASE_ID)); + r.setAttachmentName(results.getString(COL_ATTACHMENT_NAME)); + u.add(r); + } + return u; + } + }, institutionUrl, itemUuid, new OptionalParam(itemVersion, !allVersions), + new OptionalParam(available, available), new OptionalParam(available, available)); + return usages; + } + + public static List findAllUsages(final String institutionUrl, final String query, final String courseId, + String folderId, boolean available, String sortColumn, boolean sortReverse) { + BbLogger.instance().logDebug("findAllUsages(" + institutionUrl + ", " + query + ", " + courseId + ", " + folderId + + ", " + available + ", " + sortColumn + ", " + sortReverse + ")"); + ensureDatabase(); + + final StringBuilder sql = select().append(" WHERE INSTURL=?"); + + final boolean hasQuery = !Strings.isNullOrEmpty(query); + String likeQuery = null; + if (hasQuery) { + likeQuery = '%' + query.toLowerCase() + '%'; + sql.append(" AND LOWER(BBTITLE) LIKE ?"); + } + final boolean hasCourseId = !Strings.isNullOrEmpty(courseId); + if (hasCourseId) { + sql.append(" AND COURSEID = ?"); + } + final boolean hasFolderId = !Strings.isNullOrEmpty(folderId); + if (hasFolderId) { + sql.append(" AND FOLDERID = ?"); + } + if (available) { + sql.append(" AND AVAILABLE=? AND COURSEAVAILABLE=?"); + } + + String sortCol = "DTCREATED"; + String sortOrd = sortReverse ? "DESC" : "ASC"; + if (!Strings.isNullOrEmpty(sortColumn)) { + if (sortColumn.equals("name")) { + sortCol = "LOWER(BBTITLE)"; + } else if (sortColumn.equals("course")) { + sortCol = "LOWER(COURSENAME)"; + } + } + sql.append(" ORDER BY " + sortCol + " " + sortOrd); + + // @formatter:off + final List usages = SqlUtil.runSql(sql.toString(), new ResultProcessor() { + @Override + public List getResults(ResultSet results) throws SQLException { + final List u = new ArrayList(); + while (results.next()) { + final ItemInfo r = new ItemInfo(institutionUrl, results.getString(COL_UUID), results.getInt(COL_VERSION), + results.getString(COL_CONTENT_ID), results.getString(COL_COURSE_ID), results.getString(COL_FOLDER_ID), + results.getString(COL_PAGE)); + r.setCreatedDate(results.getTimestamp(COL_DATE_CREATED)); + r.setModifiedDate(results.getTimestamp(COL_DATE_MODIFIED)); + r.setAvailable(results.getBoolean(COL_AVAILABLE)); + r.setName(results.getString(COL_BLACKBOARD_TITLE)); + r.setDescription(results.getString(COL_BLACKBOARD_DESC)); + r.getItemKey().setDatabaseId(results.getInt(COL_DATABASE_ID)); + r.setDateAccessed(results.getTimestamp(COL_DATE_ACCESSED)); + r.setAttachmentName(results.getString(COL_ATTACHMENT_NAME)); + u.add(r); + } + return u; + } + }, institutionUrl, new OptionalParam(likeQuery, hasQuery), new OptionalParam(courseId, hasCourseId), + new OptionalParam(folderId, hasFolderId), new OptionalParam(available, available), + new OptionalParam(available, available)); + // @formatter:on + return usages; + } + + // Used by SynchroniseContentThread + public static List findEquellaContentByCourse(final String institutionUrl, final String courseId, + boolean available) { + BbLogger.instance() + .logDebug("findEquellaContentByCourse(" + institutionUrl + ", " + courseId + ", " + available + ")"); + ensureDatabase(); + + // ID, UUID, VERSION, FOLDERID, ATTACHMENT_NAME, CONTENTID + final StringBuilder sql = select(COL_DATABASE_ID, COL_UUID, COL_VERSION, COL_FOLDER_ID, COL_PAGE, COL_CONTENT_ID) + .append("WHERE INSTURL=? AND COURSEID=?"); + if (available) { + sql.append(" AND AVAILABLE=? AND COURSEAVAILABLE=?"); + } + + final List usages = SqlUtil.runSql(sql.toString(), new ResultProcessor() { + @Override + public List getResults(ResultSet results) throws SQLException { + List u = new ArrayList(); + while (results.next()) { + final int dbId = results.getInt(COL_DATABASE_ID); + final String uuid = results.getString(COL_UUID); + final int version = results.getInt(COL_VERSION); + final String folderId = results.getString(COL_FOLDER_ID); + final String page = results.getString(COL_PAGE); + final String contentId = results.getString(COL_CONTENT_ID); + + final ItemKey k = new ItemKey(institutionUrl, uuid, version, contentId, courseId, folderId, page); + k.setDatabaseId(dbId); + u.add(k); + } + return u; + } + }, institutionUrl, courseId, new OptionalParam(available, available), + new OptionalParam(available, available)); + return usages; + } + + public static int cleanupBadContent(final String institutionUrl) { + BbLogger.instance().logDebug("cleanupBadContent(" + institutionUrl + ")"); + ensureDatabase(); + + String sql = delete() + "WHERE INSTURL=? AND CONTENTID IS NULL"; + List updates = SqlUtil.runSql(sql, null, institutionUrl); + return updates.get(0); + } + + public static ContentRegisteredResponse contentIsRegistered(ItemKey itemKey) { + BbLogger.instance().logDebug("contentIsRegistered(" + itemKey + ")"); + ensureDatabase(); + + final ResultProcessor resultProcessor = new ResultProcessor() { + @Override + public List getResults(ResultSet results) throws SQLException { + List r = new ArrayList(); + while (results.next()) { + r.add(new Object[] { results.getInt(COL_DATABASE_ID), results.getBoolean(COL_AVAILABLE), + results.getString(COL_CONTENT_ID) }); + } + return r; + } + }; + + final List res; + final StringBuilder sql = select(COL_DATABASE_ID, COL_AVAILABLE, COL_CONTENT_ID); + if (itemKey.getContentId() != null) { + sql.append("WHERE CONTENTID=?"); + res = SqlUtil.runSql(sql.toString(), resultProcessor, itemKey.getContentId()); + } else { + // OLD STYLE + final String page = itemKey.getPage(); + sql.append("WHERE INSTURL=? AND UUID=? AND VERSION=? AND COURSEID=? AND FOLDERID=? AND PAGE" + + (Strings.isNullOrEmpty(page) ? " IS NULL" : "=?")); + + res = SqlUtil.runSql(sql.toString(), resultProcessor, itemKey.getInstitutionUrl(), itemKey.getUuid(), + itemKey.getVersion(), itemKey.getCourseId(), itemKey.getFolderId(), + new OptionalParam(page, !Strings.isNullOrEmpty(page))); + } + + final ContentRegisteredResponse response = new ContentRegisteredResponse(); + if (!res.isEmpty()) { + response.setRegistered(true); + final Object[] r = res.get(0); + response.setId((Integer) r[0]); + response.setAvailable((Boolean) r[1]); + response.setContentId((String) r[2]); + } + + return response; + } + + private static void ensureDatabase() { + if (databaseVersion < 1) { + try { + SqlUtil.runSql(SqlUtil.select("1").toString(), null); + databaseVersion = 1; + } catch (Exception e) { + makeDatabase(); + databaseVersion = 1; + } + } + + if (databaseVersion < 2) { + if (!SqlUtil.columnExists(COL_CONTENT_ID)) { + modifyDatabaseAddContentAndCourseName(); + } + databaseVersion = 2; + } + + if (databaseVersion < 3) { + if (!SqlUtil.columnExists(COL_BLACKBOARD_DESC)) { + modifyDatabaseAddDescription(); + } + databaseVersion = 3; + } + + if (databaseVersion < 4) { + if (!SqlUtil.columnExists(COL_DATE_ACCESSED)) { + modifyDatabaseAddDateAccessed(); + } + databaseVersion = 4; + } + + if (databaseVersion < 5) { + if (!SqlUtil.columnExists(COL_ATTACHMENT_NAME)) { + modifyDatabaseAddAttachmentName(); + } + databaseVersion = 5; + } + } + + /** + * Todo: this clearly isn't normalised. But the question is, is it worth it? + */ + private static void makeDatabase() { + final String sql; + if (getDatabase().isOracle()) { + BbLogger.instance().logDebug("Oracle DB detected"); + + SqlUtil.runSql("CREATE SEQUENCE equellaseq START WITH 1 INCREMENT BY 1 NOMAXVALUE", null); + + // @formatter:off + sql = "CREATE TABLE equellacontent (" + "ID NUMBER NOT NULL, " + "INSTURL NVARCHAR2(255) NOT NULL, " + + "DTCREATED DATE DEFAULT SYSDATE, " + "DTMODIFIED DATE DEFAULT SYSDATE, " + + "BBTITLE NVARCHAR2(1024) NOT NULL," + "PAGE NVARCHAR2(1024)," // nullable for item summaries + + "UUID NVARCHAR2(40) NOT NULL, " + "VERSION NUMBER NOT NULL," + "COURSEID NVARCHAR2(32) NOT NULL, " + + "FOLDERID NVARCHAR2(32) NOT NULL, " + "AVAILABLE NUMBER NOT NULL," + "COURSEAVAILABLE NUMBER NOT NULL," + + "CONSTRAINT equellacontent_pk PRIMARY KEY (ID))"; + // @formatter:on + SqlUtil.runSql(sql, null); + + SqlUtil.runSql("CREATE INDEX eqcontent_uuidversion ON equellacontent(INSTURL, UUID, VERSION)", null); + // cannot be UNIQUE since same item could be added to same folder + // (in theory) + SqlUtil.runSql("CREATE INDEX eqcontent_all ON equellacontent(INSTURL, UUID, VERSION, COURSEID, FOLDERID)", null); + SqlUtil.runSql("CREATE INDEX eqcontent_course ON equellacontent(INSTURL, COURSEID)", null); + SqlUtil.runSql("CREATE INDEX eqcontent_available ON equellacontent(AVAILABLE, COURSEAVAILABLE)", null); + } else { + BbLogger.instance().logDebug("SQL Server DB detected"); + // @formatter:off + sql = "CREATE TABLE dbo.equellacontent (" + "ID int NOT NULL PRIMARY KEY IDENTITY, " + + "INSTURL nvarchar(255) NOT NULL, " + "BBTITLE nvarchar(1024) NOT NULL, " + "PAGE nvarchar(1024) NULL, " // nullable + // for + // item + // summaries + + "DTCREATED datetime NOT NULL, " + "DTMODIFIED datetime NOT NULL, " + "UUID nvarchar(40) NOT NULL, " + + "VERSION int NOT NULL," + "COURSEID nvarchar(32) NOT NULL, " + "FOLDERID nvarchar(32) NOT NULL, " + + "AVAILABLE bit NOT NULL," + "COURSEAVAILABLE bit NOT NULL" + ")"; + // @formatter:on + SqlUtil.runSql(sql, null); + + SqlUtil.runSql("CREATE INDEX eqcontent_course ON dbo.equellacontent(INSTURL, COURSEID)", null); + // cannot be UNIQUE since same item could be added to same folder + // (in theory) + SqlUtil.runSql("CREATE INDEX eqcontent_all ON dbo.equellacontent(INSTURL, UUID, VERSION, COURSEID, FOLDERID)", + null); + SqlUtil.runSql("CREATE INDEX eqcontent_uuidversion ON dbo.equellacontent(INSTURL, UUID, VERSION)", null); + SqlUtil.runSql("CREATE INDEX eqcontent_available ON dbo.equellacontent(AVAILABLE, COURSEAVAILABLE)", null); + } + } + + private static void modifyDatabaseAddContentAndCourseName() { + if (getDatabase().isOracle()) { + // debug("Oracle DB detected"); + // @formatter:off + final String sql = "ALTER TABLE equellacontent ADD (" + "CONTENTID NVARCHAR2(32) NULL, " + + "COURSENAME NVARCHAR2(512) NULL" + ")"; + // @formatter:on + SqlUtil.runSql(sql, null); + SqlUtil.runSql("CREATE INDEX eqcontent_content ON equellacontent(INSTURL, CONTENTID)", null); + } else { + // debug("SQL Server DB detected"); + // @formatter:off + final String sql = "ALTER TABLE dbo.equellacontent ADD " + "CONTENTID nvarchar(32) NULL, " + + "COURSENAME nvarchar(512) NULL"; + // @formatter:on + SqlUtil.runSql(sql, null); + SqlUtil.runSql("CREATE INDEX eqcontent_content ON dbo.equellacontent(INSTURL, CONTENTID)", null); + } + } + + private static void modifyDatabaseAddDescription() { + final String sql; + if (getDatabase().isOracle()) { + sql = "ALTER TABLE equellacontent ADD (BBDESC NVARCHAR2(512) NULL)"; + } else { + sql = "ALTER TABLE dbo.equellacontent ADD BBDESC nvarchar(512) NULL"; + } + SqlUtil.runSql(sql, null); + } + + private static void modifyDatabaseAddDateAccessed() { + final String sql; + if (getDatabase().isOracle()) { + sql = "ALTER TABLE equellacontent ADD (" + COL_DATE_ACCESSED + " DATE NULL)"; + } else { + sql = "ALTER TABLE dbo.equellacontent ADD " + COL_DATE_ACCESSED + " datetime NULL"; + } + SqlUtil.runSql(sql, null); + } + + private static void modifyDatabaseAddAttachmentName() { + final String sql; + if (getDatabase().isOracle()) { + sql = "ALTER TABLE equellacontent ADD (" + COL_ATTACHMENT_NAME + " NVARCHAR2(512) NULL)"; + } else { + sql = "ALTER TABLE dbo.equellacontent ADD " + COL_ATTACHMENT_NAME + " nvarchar(512) NULL"; + } + SqlUtil.runSql(sql, null); + } + + private static BbDatabase getDatabase() { + final DatabaseContainer dbContainer = (DatabaseContainer) PersistenceServiceFactory.getInstance() + .getDbPersistenceManager().getContainer(); + return dbContainer.getBbDatabase(); + } + + private static String trim(String text, int maxlen) { + if (text != null && text.length() > maxlen) { + return text.substring(0, maxlen); + } + return text; + } } diff --git a/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/Configuration.java b/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/Configuration.java index d4a36d0..d877f5c 100644 --- a/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/Configuration.java +++ b/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/Configuration.java @@ -28,6 +28,8 @@ import blackboard.platform.blti.BasicLTIDomainConfigManagerFactory; import blackboard.platform.gradebook2.ScoreProvider; import blackboard.platform.gradebook2.impl.ScoreProviderDAO; +import blackboard.platform.log.LogService; +import blackboard.platform.log.LogServiceFactory; import blackboard.platform.plugin.ContentHandler; import blackboard.platform.plugin.ContentHandlerDbLoader; import blackboard.platform.plugin.ContentHandlerDbPersister; @@ -40,6 +42,8 @@ import com.google.common.base.Throwables; import com.google.common.io.Closeables; import org.apereo.openequella.integration.blackboard.common.BbContext; +import org.apereo.openequella.integration.blackboard.common.BbLogger; +import org.apereo.openequella.integration.blackboard.common.BbLogger.LogLevel; import org.apereo.openequella.integration.blackboard.common.BbUtil; import org.apereo.openequella.integration.blackboard.common.PathUtils; import org.apereo.openequella.integration.blackboard.common.content.PlacementUtil; @@ -48,570 +52,597 @@ @SuppressWarnings("nls") // @NonNullByDefault public class Configuration { - /* @Nullable */ - private static Configuration instance; - private static final Object instanceLock = new Object(); - - private static final String CONFIG_FILE = "config.properties"; - private static final String CONTENT_HANDLER_HANDLE = "resource/tle-resource"; - - // DEPRECATED CONFIG ELEMENTS - private static final String HOST = "host"; - private static final String PORT = "port"; - private static final String CONTEXT = "context"; - private static final String INSTITUTION = "institution"; - private static final String SECURE = "secure"; - private static final String MOCK_PORTAL_ROLES = "mock.portal.roles"; - - // CONFIG ELEMENTS (referred to in the JSP) - public static final String EQUELLA_URL = "equellaurl"; - public static final String SECRET = "secret"; - public static final String SECRETID = "secretid"; - public static final String OAUTH_CLIENT_ID = "oauth.clientid"; - public static final String OAUTH_CLIENT_SECRET = "oauth.clientsecret"; - public static final String RESTRICTIONS = "restrictions"; - public static final String NEWWINDOW = "newwindow"; - - private static final String DEFAULT_SECRET = ""; - private static final String DEFAULT_RESTRICTION = "none"; - - private final BbContext context; - private final PlugInConfig plugin; - /* @Nullable */ - private ContentHandler contentHandler; - private final Object contentHandlerLock = new Object(); - - /* @Nullable */ - private String version; - /* @Nullable */ - private String equellaUrl; - /* @Nullable */ - private String secret; - /* @Nullable */ - private String secretid; - /* @Nullable */ - private String oauthClientId; - /* @Nullable */ - private String oauthClientSecret; - /* @Nullable */ - private String restriction; - private boolean newWindow; - /* @Nullable */ - private Set mockPortalRoles; - - private Date lastModified = Calendar.getInstance().getTime(); - - @SuppressWarnings("null") - public static Configuration instance() { - if (instance == null) { - synchronized (instanceLock) { - if (instance == null) { - instance = new Configuration(); - } - } - } - return instance; - } - - private Configuration() { - try { - plugin = new PlugInConfig(BbUtil.VENDOR, BbUtil.HANDLE); - context = BbContext.instance(); - - final VirtualInstallationManager vim = VirtualInstallationManagerFactory.getInstance(); - final VirtualHost vhost = vim.getVirtualHost(""); - context.getContextManager().setContext(vhost); - - version = loadVersion(); - BbUtil.trace("Version: " + version); - - load(); - ensureLtiPlacement(); - ensureScoreProvider(); - // Fix up dodgy Blind SSL - // HttpsURLConnection.setDefaultSSLSocketFactory(new - // sun.security.ssl.SSLSocketFactoryImpl()); - } catch (Exception e) { - BbUtil.error("Couldn't init building block", e); - throw Throwables.propagate(e); - } - } - - @Override - protected void finalize() throws Throwable { - context.getContextManager().releaseContext(); - super.finalize(); - } - - public synchronized void modify(HttpServletRequest request) throws Exception { - setEquellaUrl(request.getParameter(EQUELLA_URL)); - setSecret(request.getParameter(SECRET)); - setSecretId(request.getParameter(SECRETID)); - setOauthClientId(request.getParameter(OAUTH_CLIENT_ID)); - setOauthClientSecret(request.getParameter(OAUTH_CLIENT_SECRET)); - setRestriction(request.getParameter(RESTRICTIONS)); - String newWindowParam = request.getParameter(NEWWINDOW); - if (newWindowParam == null || newWindowParam.equals("")) { - newWindowParam = "false"; - } - setNewWindow(Boolean.parseBoolean(newWindowParam)); - lastModified = Calendar.getInstance().getTime(); - } - - public synchronized void load() { - final File configFile = new File(getConfigDirectory(), CONFIG_FILE); - if (!configFile.exists()) { - try { - configFile.createNewFile(); - BbUtil.trace("Successfully created configuration file"); - } catch (IOException e) { - BbUtil.error("Error creating configuration file", e); - throw Throwables.propagate(e); - } - return; - } - - FileInputStream fis = null; - try { - fis = new FileInputStream(configFile); - final Properties props = new Properties(); - props.load(fis); - if (props.containsKey(EQUELLA_URL)) { - setEquellaUrl(props.getProperty(EQUELLA_URL)); - } else { - try { - setEquellaUrl(buildEquellaUrlFromDeprecatedConfigParams(props)); - } catch (MalformedURLException mal) { - BbUtil.trace("Failed to load equella URL from deprecated props"); - } - } - setSecret(props.getProperty(SECRET)); - setSecretId(props.getProperty(SECRETID)); - setOauthClientId(props.getProperty(OAUTH_CLIENT_ID)); - setOauthClientSecret(props.getProperty(OAUTH_CLIENT_SECRET)); - setMockPortalRoles(commaSplit(props.getProperty(MOCK_PORTAL_ROLES))); - setRestriction(props.getProperty(RESTRICTIONS)); - setNewWindow(Boolean.parseBoolean(props.getProperty(NEWWINDOW, "true"))); - } catch (Exception e) { - BbUtil.error("Error loading configuration", e); - throw Throwables.propagate(e); - } finally { - if (fis != null) { - try { - fis.close(); - } catch (IOException e) { - // Ignore - } - } - } - } - - public synchronized void save() { - final File configFile = new File(getConfigDirectory(), CONFIG_FILE); - FileOutputStream fos = null; - try { - ensureLtiPlacement(); - ensureScoreProvider(); - - fos = new FileOutputStream(configFile); - - Properties props = new Properties(); - props.setProperty(EQUELLA_URL, equellaUrl); - props.setProperty(SECRET, secret); - props.setProperty(SECRETID, secretid); - props.setProperty(OAUTH_CLIENT_ID, oauthClientId); - props.setProperty(OAUTH_CLIENT_SECRET, oauthClientSecret); - props.setProperty(MOCK_PORTAL_ROLES, commaJoin(mockPortalRoles)); - props.setProperty(RESTRICTIONS, restriction); - props.setProperty(NEWWINDOW, Boolean.toString(newWindow)); - props.store(fos, null); - } catch (Exception e) { - BbUtil.error("Error saving configuration", e); - throw Throwables.propagate(e); - } finally { - if (fos != null) { - try { - fos.close(); - } catch (IOException e) { - // Ignore - } - } - } - } - - /** - * @Nullable - * @return - */ - private synchronized BasicLTIPlacement ensureLtiPlacement() { - try { - BbUtil.trace("ensureLtiPlacement"); - if (Strings.isNullOrEmpty(oauthClientId) || Strings.isNullOrEmpty(oauthClientSecret) - || Strings.isNullOrEmpty(equellaUrl)) { - BbUtil.trace("Not creating a placement since a property was blank"); - return null; - } - - BasicLTIDomainConfig domainConfig = null; - boolean newPlacement = false; - BasicLTIPlacement placement = PlacementUtil.loadFromHandle(CONTENT_HANDLER_HANDLE); - if (placement == null) { - BbUtil.trace("Loading placement via URL " + equellaUrl); - final LoadPlacementResponse loadPlacement = PlacementUtil.loadPlacementByUrl(equellaUrl); - placement = loadPlacement.getPlacement(); - domainConfig = loadPlacement.getDomainConfig(); - if (placement == null) { - BbUtil.trace("No existing placement for URL " + equellaUrl); - final Id placementId = getContentHandler().getBasicLTIPlacementId(); - if (!Id.isValid(placementId)) { - BbUtil.trace("No existing placement associated with ContentHandler"); - - if (domainConfig == null) { - domainConfig = createDomainConfig(); - } - placement = PlacementUtil.createNewPlacement(domainConfig, getContentHandler()); - newPlacement = true; - } else { - BbUtil.trace("Loading existing placement from ContentHandler"); - placement = PlacementUtil.loadFromId(placementId); - if (placement == null) { - BbUtil.trace("Content handler pointing to invalid placement..."); - if (domainConfig == null) { - domainConfig = createDomainConfig(); - } - placement = PlacementUtil.createNewPlacement(domainConfig, getContentHandler()); - newPlacement = true; - } - } - } else { - BbUtil.trace("Loaded placement via URL"); - } - } else { - BbUtil.trace("Loaded placement via handle"); - } - - // ensure domainConfig - if (domainConfig == null) { - BbUtil.trace("No domain config loaded"); - - // get any current domain config for this domain - domainConfig = PlacementUtil.loadDomainConfigByUrl(new URL(equellaUrl)); - - if (domainConfig != null) { - BbUtil.trace("Domain config loaded via URL " + equellaUrl); - if (populateDomainConfig(domainConfig)) { - BbUtil.trace("Saving dirty domain config"); - final BasicLTIDomainConfigManager fac = BasicLTIDomainConfigManagerFactory.getInstance(); - fac.save(domainConfig); - } else { - BbUtil.trace("Not saving doman config (not dirty)"); - } - } else { - BbUtil.trace("No domain config for URL " + equellaUrl); - final Id domainConfigId = placement.getBasicLTIDomainConfigId(); - if (Id.isValid(domainConfigId)) { - BbUtil.trace("Loading domain config based on placement pointer"); - final BasicLTIDomainConfigManager fac = BasicLTIDomainConfigManagerFactory.getInstance(); - domainConfig = fac.loadById(domainConfigId); - if (populateDomainConfig(domainConfig)) { - BbUtil.trace("Saving dirty domain config"); - fac.save(domainConfig); - } else { - BbUtil.trace("Not saving domain config (not dirty)"); - } - } else { - BbUtil.trace("Creating new domain config"); - domainConfig = createDomainConfig(); - } - } - } - - BbUtil.trace("newPlacement = " + newPlacement); - BbUtil.trace("placement = " + placement.getId().toExternalString()); - - boolean saveHandler = newPlacement; - // delete the old placement associated with this handler if - // there is one - final ContentHandler handler = getContentHandler(); - if (handlerPlacementMismatch(placement, handler)) { - if (Id.isValid(handler.getBasicLTIPlacementId())) { - BbUtil.trace("Deleting existing placement associated with this handler " - + handler.getBasicLTIPlacementId().toExternalString()); - PlacementUtil.deleteById(handler.getBasicLTIPlacementId()); - } - saveHandler = true; - } - - // Save placement? - // if( newPlacement || requiresSave(placement, domainConfig) ) - // { - placement.setBasicLTIDomainConfigId(domainConfig.getId()); - placement.setUrl(equellaUrl); - PlacementUtil.save(placement); - // } - - // Save handler? - if (saveHandler) { - BbUtil.trace("Saving updated ContentHandler with placement " + placement.getId().toExternalString()); - - handler.setBasicLTIPlacementId(placement.getId()); - final ContentHandlerDbPersister contentHandlerPersister = (ContentHandlerDbPersister) BbContext.instance() - .getPersistenceManager().getPersister(ContentHandlerDbPersister.TYPE); - contentHandlerPersister.persist(handler); - } else { - BbUtil.trace("Not saving ContentHandler"); - } - - return placement; - } catch (Exception e) { - BbUtil.error("Error ensuring LTI placement", e); - throw Throwables.propagate(e); - } - } - - private boolean handlerPlacementMismatch(BasicLTIPlacement placement, ContentHandler handler) { - final Id currentPlacementId = handler.getBasicLTIPlacementId(); - if (!currentPlacementId.equals(placement.getId())) { - BbUtil.trace("ContentHandler is pointing at a different (or null) placement " - + handler.getBasicLTIPlacementId().toExternalString()); - return true; - } - BbUtil.trace("ContentHandler is pointing to correct placement"); - return false; - } - - private synchronized ScoreProvider ensureScoreProvider() { - final ScoreProviderDAO dao = ScoreProviderDAO.get(); - ScoreProvider provider = dao.getByHandle(BbUtil.CONTENT_HANDLER); - if (provider == null) { - // The boolean values are cloned from what I could find about - // resource/x-bb-blti-link - // in score_provider.txt in BB installer - provider = new ScoreProvider(); - provider.setName("Equella"); - provider.setHandle(BbUtil.CONTENT_HANDLER); - provider.setAllowAttemptGrading(true); - provider.setAllowMultiple(false); - provider.setAttemptBased(false); - provider.setGradeAction(PathUtils.urlPath(BbUtil.getBlockRelativePath(), "ViewGradebook")); - provider.setReviewAction(PathUtils.urlPath(BbUtil.getBlockRelativePath(), "ViewGradebook")); - dao.persist(provider); - } - - return provider; - } - - private BasicLTIDomainConfig createDomainConfig() { - final BasicLTIDomainConfig domainConfig = new BasicLTIDomainConfig(); - populateDomainConfig(domainConfig); - domainConfig.setSendEmail(true); - domainConfig.setSendName(true); - domainConfig.setSendRole(true); - domainConfig.setUseSplash(false); - domainConfig.setSendUserData(SendUserData.Always); - domainConfig.setStatus(Status.Approved); - final BasicLTIDomainConfigManager fac = BasicLTIDomainConfigManagerFactory.getInstance(); - fac.save(domainConfig); - return domainConfig; - } - - /** - * @param domainConfig - * @return Was changed - */ - private boolean populateDomainConfig(BasicLTIDomainConfig domainConfig) { - try { - boolean dirty = false; - final String key = domainConfig.getKey(); - if (!Strings.nullToEmpty(key).equals(Strings.nullToEmpty(oauthClientId))) { - domainConfig.setKey(oauthClientId); - dirty = true; - } - final String secret = domainConfig.getSecret(); - if (!Strings.nullToEmpty(secret).equals(Strings.nullToEmpty(oauthClientSecret))) { - domainConfig.setSecret(oauthClientSecret); - dirty = true; - } - - BasicLTIDomainHost host = domainConfig.getPrimaryHost(); - final String newDomain = new URL(equellaUrl).getHost(); - if (host == null || !Strings.nullToEmpty(host.getDomain()).equals(Strings.nullToEmpty(newDomain))) { - host = (host == null ? new BasicLTIDomainHost() : host); - host.setDomain(newDomain); - host.setPrimary(true); - domainConfig.setPrimaryHost(host); - dirty = true; - } - return dirty; - } catch (Exception mal) { - BbUtil.error("Error populating domainConfig", mal); - throw Throwables.propagate(mal); - } - } - - private ContentHandler getContentHandler() throws Exception { - if (contentHandler == null) { - synchronized (contentHandlerLock) { - if (contentHandler == null) { - final ContentHandlerDbLoader contentHandlerLoader = (ContentHandlerDbLoader) BbContext.instance() - .getPersistenceManager().getLoader(ContentHandlerDbLoader.TYPE); - contentHandler = contentHandlerLoader.loadByHandle(CONTENT_HANDLER_HANDLE); - final Id basicLTIPlacementId = contentHandler.getBasicLTIPlacementId(); - BbUtil.trace("Loaded content handler from DB, placement = " - + (basicLTIPlacementId == null ? "null" : basicLTIPlacementId.toExternalString())); - } - } - } - return contentHandler; - } - - public static String loadVersion() throws IOException { - InputStream in = null; - try { - final Properties p = new Properties(); - in = Configuration.class.getResourceAsStream("/version.properties"); - p.load(in); - final String mmr = p.getProperty("version.mmr"); - final String display = p.getProperty("version.display"); - return MessageFormat.format("{0} ({1})", mmr, display); - } catch (Exception e) { - BbUtil.error("Couldn't load version", e); - throw Throwables.propagate(e); - } finally { - Closeables.close(in, false); - } - } - - public File getConfigDirectory() { - return plugin.getConfigDirectory(); - } - - private String buildEquellaUrlFromDeprecatedConfigParams(Properties props) throws MalformedURLException { - final boolean secure = Boolean.valueOf(props.getProperty(SECURE)); - final String host = props.getProperty(HOST, "localhost"); - final int port = Integer.parseInt(props.getProperty(PORT, "80")); - final String context = props.getProperty(CONTEXT, "/"); - final String inst = props.getProperty(INSTITUTION, ""); - - return new URL(new URL(secure ? "https" : "http", host, port, context), inst).toString(); - } - - public boolean hasBeenModified(Date lastUpdate) { - return lastUpdate.before(lastModified); - } - - private Set commaSplit(/* @Nullable */String value) { - final Set result = new HashSet(); - if (value != null) { - final String[] vs = value.split(","); - for (int i = 0; i < vs.length; i++) { - result.add(vs[i].trim()); - } - } - return result; - } - - private String commaJoin(/* @Nullable */Collection values) { - final StringBuilder roles = new StringBuilder(); - if (values != null) { - for (Iterator iter = values.iterator(); iter.hasNext();) { - if (roles.length() > 0) { - roles.append(','); - } - roles.append(iter.next()); - } - } - return roles.toString(); - } - - /* @Nullable */ - public String getEquellaUrl() { - return equellaUrl; - } - - public void setEquellaUrl(/* @Nullable */String equellaUrl) { - this.equellaUrl = (equellaUrl == null ? null : equellaUrl.trim()); - if (!Strings.isNullOrEmpty(this.equellaUrl) && !this.equellaUrl.endsWith("/")) { - this.equellaUrl += '/'; - } - } - - /* @Nullable */ - public String getSecret() { - return secret; - } - - public void setSecret(/* @Nullable */String secret) { - if (secret == null || secret.length() == 0) { - this.secret = DEFAULT_SECRET; - } else { - this.secret = secret; - } - } - - /* @Nullable */ - public String getSecretId() { - return secretid; - } - - public void setSecretId(/* @Nullable */String secretid) { - if (secretid == null || secretid.length() == 0) { - this.secretid = DEFAULT_SECRET; - } else { - this.secretid = secretid; - } - } - - /* @Nullable */ - public String getOauthClientId() { - return oauthClientId; - } - - public void setOauthClientId(/* @Nullable */String oauthClientId) { - this.oauthClientId = oauthClientId; - } - - /* @Nullable */ - public String getOauthClientSecret() { - return oauthClientSecret; - } - - public void setOauthClientSecret(/* @Nullable */String oauthClientSecret) { - this.oauthClientSecret = oauthClientSecret; - } - - /* @Nullable */ - public String getVersion() { - return version; - } - - public void setMockPortalRoles(/* @Nullable */Set mockPortalRoles) { - this.mockPortalRoles = mockPortalRoles; - } - - /* @Nullable */ - public Set getMockPortalRoles() { - return mockPortalRoles; - } - - /* @Nullable */ - public String getRestriction() { - return restriction; - } - - public void setRestriction(/* @Nullable */String restriction) { - if (restriction == null) { - this.restriction = DEFAULT_RESTRICTION; - } else { - this.restriction = restriction; - } - } - - public void setNewWindow(boolean newWindow) { - this.newWindow = newWindow; - } - - public boolean isNewWindow() { - return newWindow; - } + + /* @Nullable */ + private static Configuration instance; + private static final Object instanceLock = new Object(); + + private static final String CONFIG_FILE = "config.properties"; + private static final String CONTENT_HANDLER_HANDLE = "resource/tle-resource"; + + // DEPRECATED CONFIG ELEMENTS + private static final String HOST = "host"; + private static final String PORT = "port"; + private static final String CONTEXT = "context"; + private static final String INSTITUTION = "institution"; + private static final String SECURE = "secure"; + private static final String MOCK_PORTAL_ROLES = "mock.portal.roles"; + + // CONFIG ELEMENTS (referred to in the JSP) + public static final String EQUELLA_URL = "equellaurl"; + public static final String SECRET = "secret"; + public static final String SECRETID = "secretid"; + public static final String OAUTH_CLIENT_ID = "oauth.clientid"; + public static final String OAUTH_CLIENT_SECRET = "oauth.clientsecret"; + public static final String RESTRICTIONS = "restrictions"; + public static final String LOG_LEVEL = "loglevel"; + public static final String NEWWINDOW = "newwindow"; + + private static final String DEFAULT_SECRET = ""; + private static final String DEFAULT_RESTRICTION = "none"; + private static final LogLevel DEFAULT_LOG_LEVEL = BbLogger.LogLevel.Info; + + private final BbContext context; + private final PlugInConfig plugin; + /* @Nullable */ + private ContentHandler contentHandler; + private final Object contentHandlerLock = new Object(); + + /* @Nullable */ + private String version; + /* @Nullable */ + private String equellaUrl; + /* @Nullable */ + private String secret; + /* @Nullable */ + private String secretid; + /* @Nullable */ + private String oauthClientId; + /* @Nullable */ + private String oauthClientSecret; + /* @Nullable */ + private String restriction; + /* @Nullable */ + private String logLevel; + private boolean newWindow; + /* @Nullable */ + private Set mockPortalRoles; + + private Date lastModified = Calendar.getInstance().getTime(); + + @SuppressWarnings("null") + public static Configuration instance() { + if (instance == null) { + synchronized (instanceLock) { + if (instance == null) { + instance = new Configuration(); + } + } + } + return instance; + } + + private Configuration() { + try { + plugin = new PlugInConfig(BbUtil.VENDOR, BbUtil.HANDLE); + context = BbContext.instance(); + + final VirtualInstallationManager vim = VirtualInstallationManagerFactory.getInstance(); + final VirtualHost vhost = vim.getVirtualHost(""); + context.getContextManager().setContext(vhost); + + version = loadVersion(); + BbLogger.instance().logTrace("Version: " + version); + + load(); + ensureLtiPlacement(); + ensureScoreProvider(); + // Fix up dodgy Blind SSL + // HttpsURLConnection.setDefaultSSLSocketFactory(new + // sun.security.ssl.SSLSocketFactoryImpl()); + } catch (Exception e) { + BbLogger.instance().logError("Couldn't init building block", e); + throw Throwables.propagate(e); + } + } + + @Override + protected void finalize() throws Throwable { + context.getContextManager().releaseContext(); + super.finalize(); + } + + public synchronized void modify(HttpServletRequest request) throws Exception { + setEquellaUrl(request.getParameter(EQUELLA_URL)); + setSecret(request.getParameter(SECRET)); + setSecretId(request.getParameter(SECRETID)); + setOauthClientId(request.getParameter(OAUTH_CLIENT_ID)); + setOauthClientSecret(request.getParameter(OAUTH_CLIENT_SECRET)); + setRestriction(request.getParameter(RESTRICTIONS)); + setLogLevel(request.getParameter(LOG_LEVEL)); + String newWindowParam = request.getParameter(NEWWINDOW); + if (newWindowParam == null || newWindowParam.equals("")) { + newWindowParam = "false"; + } + setNewWindow(Boolean.parseBoolean(newWindowParam)); + lastModified = Calendar.getInstance().getTime(); + + } + + public synchronized void load() { + final File configFile = new File(getConfigDirectory(), CONFIG_FILE); + if (!configFile.exists()) { + try { + configFile.createNewFile(); + BbLogger.instance().logTrace("Successfully created configuration file"); + } catch (IOException e) { + BbLogger.instance().logError("Error creating configuration file", e); + throw Throwables.propagate(e); + } + return; + } + + FileInputStream fis = null; + try { + fis = new FileInputStream(configFile); + final Properties props = new Properties(); + props.load(fis); + if (props.containsKey(EQUELLA_URL)) { + setEquellaUrl(props.getProperty(EQUELLA_URL)); + } else { + try { + setEquellaUrl(buildEquellaUrlFromDeprecatedConfigParams(props)); + } catch (MalformedURLException mal) { + BbLogger.instance().logTrace("Failed to load equella URL from deprecated props"); + } + } + setSecret(props.getProperty(SECRET)); + setSecretId(props.getProperty(SECRETID)); + setOauthClientId(props.getProperty(OAUTH_CLIENT_ID)); + setOauthClientSecret(props.getProperty(OAUTH_CLIENT_SECRET)); + setMockPortalRoles(commaSplit(props.getProperty(MOCK_PORTAL_ROLES))); + setRestriction(props.getProperty(RESTRICTIONS)); + setLogLevel(props.getProperty(LOG_LEVEL)); + setNewWindow(Boolean.parseBoolean(props.getProperty(NEWWINDOW, "true"))); + } catch (Exception e) { + BbLogger.instance().logError("Error loading configuration", e); + throw Throwables.propagate(e); + } finally { + if (fis != null) { + try { + fis.close(); + } catch (IOException e) { + // Ignore + } + } + } + } + + public synchronized void save() { + final File configFile = new File(getConfigDirectory(), CONFIG_FILE); + FileOutputStream fos = null; + try { + ensureLtiPlacement(); + ensureScoreProvider(); + + fos = new FileOutputStream(configFile); + + Properties props = new Properties(); + props.setProperty(EQUELLA_URL, equellaUrl); + props.setProperty(SECRET, secret); + props.setProperty(SECRETID, secretid); + props.setProperty(OAUTH_CLIENT_ID, oauthClientId); + props.setProperty(OAUTH_CLIENT_SECRET, oauthClientSecret); + props.setProperty(MOCK_PORTAL_ROLES, commaJoin(mockPortalRoles)); + props.setProperty(RESTRICTIONS, restriction); + props.setProperty(LOG_LEVEL, logLevel); + props.setProperty(NEWWINDOW, Boolean.toString(newWindow)); + props.store(fos, null); + } catch (Exception e) { + BbLogger.instance().logError("Error saving configuration", e); + throw Throwables.propagate(e); + } finally { + if (fos != null) { + try { + fos.close(); + } catch (IOException e) { + // Ignore + } + } + } + } + + /** + * @Nullable + * @return + */ + private synchronized BasicLTIPlacement ensureLtiPlacement() { + try { + BbLogger.instance().logTrace("Entering ensureLtiPlacement"); + if (Strings.isNullOrEmpty(oauthClientId) || Strings.isNullOrEmpty(oauthClientSecret) + || Strings.isNullOrEmpty(equellaUrl)) { + BbLogger.instance().logTrace("Not creating a placement since a property was blank"); + return null; + } + + BasicLTIDomainConfig domainConfig = null; + boolean newPlacement = false; + BasicLTIPlacement placement = PlacementUtil.loadFromHandle(CONTENT_HANDLER_HANDLE); + if (placement == null) { + BbLogger.instance().logTrace("Loading placement via URL " + equellaUrl); + final LoadPlacementResponse loadPlacement = PlacementUtil.loadPlacementByUrl(equellaUrl); + placement = loadPlacement.getPlacement(); + domainConfig = loadPlacement.getDomainConfig(); + if (placement == null) { + BbLogger.instance().logTrace("No existing placement for URL " + equellaUrl); + final Id placementId = getContentHandler().getBasicLTIPlacementId(); + if (!Id.isValid(placementId)) { + BbLogger.instance().logTrace("No existing placement associated with ContentHandler"); + + if (domainConfig == null) { + domainConfig = createDomainConfig(); + } + placement = PlacementUtil.createNewPlacement(domainConfig, getContentHandler()); + newPlacement = true; + } else { + BbLogger.instance().logTrace("Loading existing placement from ContentHandler"); + placement = PlacementUtil.loadFromId(placementId); + if (placement == null) { + BbLogger.instance().logTrace("Content handler pointing to invalid placement..."); + if (domainConfig == null) { + domainConfig = createDomainConfig(); + } + placement = PlacementUtil.createNewPlacement(domainConfig, getContentHandler()); + newPlacement = true; + } + } + } else { + BbLogger.instance().logTrace("Loaded placement via URL"); + } + } else { + BbLogger.instance().logTrace("Loaded placement via handle"); + } + + // ensure domainConfig + if (domainConfig == null) { + BbLogger.instance().logTrace("No domain config loaded"); + + // get any current domain config for this domain + domainConfig = PlacementUtil.loadDomainConfigByUrl(new URL(equellaUrl)); + + if (domainConfig != null) { + BbLogger.instance().logTrace("Domain config loaded via URL " + equellaUrl); + if (populateDomainConfig(domainConfig)) { + BbLogger.instance().logTrace("Saving dirty domain config"); + final BasicLTIDomainConfigManager fac = BasicLTIDomainConfigManagerFactory.getInstance(); + fac.save(domainConfig); + } else { + BbLogger.instance().logTrace("Not saving doman config (not dirty)"); + } + } else { + BbLogger.instance().logTrace("No domain config for URL " + equellaUrl); + final Id domainConfigId = placement.getBasicLTIDomainConfigId(); + if (Id.isValid(domainConfigId)) { + BbLogger.instance().logTrace("Loading domain config based on placement pointer"); + final BasicLTIDomainConfigManager fac = BasicLTIDomainConfigManagerFactory.getInstance(); + domainConfig = fac.loadById(domainConfigId); + if (populateDomainConfig(domainConfig)) { + BbLogger.instance().logTrace("Saving dirty domain config"); + fac.save(domainConfig); + } else { + BbLogger.instance().logTrace("Not saving domain config (not dirty)"); + } + } else { + BbLogger.instance().logTrace("Creating new domain config"); + domainConfig = createDomainConfig(); + } + } + } + + BbLogger.instance().logTrace("newPlacement = " + newPlacement); + BbLogger.instance().logTrace("placement = " + placement.getId().toExternalString()); + + boolean saveHandler = newPlacement; + // delete the old placement associated with this handler if + // there is one + final ContentHandler handler = getContentHandler(); + if (handlerPlacementMismatch(placement, handler)) { + if (Id.isValid(handler.getBasicLTIPlacementId())) { + BbLogger.instance().logTrace("Deleting existing placement associated with this handler " + + handler.getBasicLTIPlacementId().toExternalString()); + PlacementUtil.deleteById(handler.getBasicLTIPlacementId()); + } + saveHandler = true; + } + + // Save placement? + // if( newPlacement || requiresSave(placement, domainConfig) ) + // { + placement.setBasicLTIDomainConfigId(domainConfig.getId()); + placement.setUrl(equellaUrl); + PlacementUtil.save(placement); + // } + + // Save handler? + if (saveHandler) { + BbLogger.instance() + .logTrace("Saving updated ContentHandler with placement " + placement.getId().toExternalString()); + + handler.setBasicLTIPlacementId(placement.getId()); + final ContentHandlerDbPersister contentHandlerPersister = (ContentHandlerDbPersister) BbContext.instance() + .getPersistenceManager().getPersister(ContentHandlerDbPersister.TYPE); + contentHandlerPersister.persist(handler); + } else { + BbLogger.instance().logTrace("Not saving ContentHandler"); + } + + return placement; + } catch (Exception e) { + BbLogger.instance().logError("Error ensuring LTI placement", e); + throw Throwables.propagate(e); + } + } + + private boolean handlerPlacementMismatch(BasicLTIPlacement placement, ContentHandler handler) { + final Id currentPlacementId = handler.getBasicLTIPlacementId(); + if (!currentPlacementId.equals(placement.getId())) { + BbLogger.instance().logTrace("ContentHandler is pointing at a different (or null) placement " + + handler.getBasicLTIPlacementId().toExternalString()); + return true; + } + BbLogger.instance().logTrace("ContentHandler is pointing to correct placement"); + return false; + } + + private synchronized ScoreProvider ensureScoreProvider() { + final ScoreProviderDAO dao = ScoreProviderDAO.get(); + ScoreProvider provider = dao.getByHandle(BbUtil.CONTENT_HANDLER); + if (provider == null) { + // The boolean values are cloned from what I could find about + // resource/x-bb-blti-link + // in score_provider.txt in BB installer + provider = new ScoreProvider(); + provider.setName("Equella"); + provider.setHandle(BbUtil.CONTENT_HANDLER); + provider.setAllowAttemptGrading(true); + provider.setAllowMultiple(false); + provider.setAttemptBased(false); + provider.setGradeAction(PathUtils.urlPath(BbUtil.getBlockRelativePath(), "ViewGradebook")); + provider.setReviewAction(PathUtils.urlPath(BbUtil.getBlockRelativePath(), "ViewGradebook")); + dao.persist(provider); + } + + return provider; + } + + private BasicLTIDomainConfig createDomainConfig() { + final BasicLTIDomainConfig domainConfig = new BasicLTIDomainConfig(); + populateDomainConfig(domainConfig); + domainConfig.setSendEmail(true); + domainConfig.setSendName(true); + domainConfig.setSendRole(true); + domainConfig.setUseSplash(false); + domainConfig.setSendUserData(SendUserData.Always); + domainConfig.setStatus(Status.Approved); + final BasicLTIDomainConfigManager fac = BasicLTIDomainConfigManagerFactory.getInstance(); + fac.save(domainConfig); + return domainConfig; + } + + /** + * @param domainConfig + * @return Was changed + */ + private boolean populateDomainConfig(BasicLTIDomainConfig domainConfig) { + try { + boolean dirty = false; + final String key = domainConfig.getKey(); + if (!Strings.nullToEmpty(key).equals(Strings.nullToEmpty(oauthClientId))) { + domainConfig.setKey(oauthClientId); + dirty = true; + } + final String secret = domainConfig.getSecret(); + if (!Strings.nullToEmpty(secret).equals(Strings.nullToEmpty(oauthClientSecret))) { + domainConfig.setSecret(oauthClientSecret); + dirty = true; + } + + BasicLTIDomainHost host = domainConfig.getPrimaryHost(); + final String newDomain = new URL(equellaUrl).getHost(); + if (host == null || !Strings.nullToEmpty(host.getDomain()).equals(Strings.nullToEmpty(newDomain))) { + host = (host == null ? new BasicLTIDomainHost() : host); + host.setDomain(newDomain); + host.setPrimary(true); + domainConfig.setPrimaryHost(host); + dirty = true; + } + return dirty; + } catch (Exception mal) { + BbLogger.instance().logError("Error populating domainConfig", mal); + throw Throwables.propagate(mal); + } + } + + private ContentHandler getContentHandler() throws Exception { + if (contentHandler == null) { + synchronized (contentHandlerLock) { + if (contentHandler == null) { + final ContentHandlerDbLoader contentHandlerLoader = (ContentHandlerDbLoader) BbContext.instance() + .getPersistenceManager().getLoader(ContentHandlerDbLoader.TYPE); + contentHandler = contentHandlerLoader.loadByHandle(CONTENT_HANDLER_HANDLE); + final Id basicLTIPlacementId = contentHandler.getBasicLTIPlacementId(); + BbLogger.instance().logTrace("Loaded content handler from DB, placement = " + + (basicLTIPlacementId == null ? "null" : basicLTIPlacementId.toExternalString())); + } + } + } + return contentHandler; + } + + public static String loadVersion() throws IOException { + InputStream in = null; + try { + // CBEACH: This file wasn't able to be read. maybe a permissions or location + // issue. + // final Properties p = new Properties(); + // in = Configuration.class.getResourceAsStream("/version.properties"); + // p.load(in); + // final String versionB2 = p.getProperty("version.b2"); + // return versionB2; + BbLogger.instance().logTrace("Loading Version."); + return "TODO - need to implement version from file system property."; + } catch (Exception e) { + BbLogger.instance().logError("Couldn't load version", e); + throw Throwables.propagate(e); + } finally { + Closeables.close(in, false); + } + } + + public File getConfigDirectory() { + return plugin.getConfigDirectory(); + } + + private String buildEquellaUrlFromDeprecatedConfigParams(Properties props) throws MalformedURLException { + final boolean secure = Boolean.valueOf(props.getProperty(SECURE)); + final String host = props.getProperty(HOST, "localhost"); + final int port = Integer.parseInt(props.getProperty(PORT, "80")); + final String context = props.getProperty(CONTEXT, "/"); + final String inst = props.getProperty(INSTITUTION, ""); + + return new URL(new URL(secure ? "https" : "http", host, port, context), inst).toString(); + } + + public boolean hasBeenModified(Date lastUpdate) { + return lastUpdate.before(lastModified); + } + + private Set commaSplit(/* @Nullable */String value) { + final Set result = new HashSet(); + if (value != null) { + final String[] vs = value.split(","); + for (int i = 0; i < vs.length; i++) { + result.add(vs[i].trim()); + } + } + return result; + } + + private String commaJoin(/* @Nullable */Collection values) { + final StringBuilder roles = new StringBuilder(); + if (values != null) { + for (Iterator iter = values.iterator(); iter.hasNext();) { + if (roles.length() > 0) { + roles.append(','); + } + roles.append(iter.next()); + } + } + return roles.toString(); + } + + /* @Nullable */ + public String getEquellaUrl() { + return equellaUrl; + } + + public void setEquellaUrl(/* @Nullable */String equellaUrl) { + this.equellaUrl = (equellaUrl == null ? null : equellaUrl.trim()); + if (!Strings.isNullOrEmpty(this.equellaUrl) && !this.equellaUrl.endsWith("/")) { + this.equellaUrl += '/'; + } + } + + /* @Nullable */ + public String getSecret() { + return secret; + } + + public void setSecret(/* @Nullable */String secret) { + if (secret == null || secret.length() == 0) { + this.secret = DEFAULT_SECRET; + } else { + this.secret = secret; + } + } + + /* @Nullable */ + public String getSecretId() { + return secretid; + } + + public void setSecretId(/* @Nullable */String secretid) { + if (secretid == null || secretid.length() == 0) { + this.secretid = DEFAULT_SECRET; + } else { + this.secretid = secretid; + } + } + + /* @Nullable */ + public String getOauthClientId() { + return oauthClientId; + } + + public void setOauthClientId(/* @Nullable */String oauthClientId) { + this.oauthClientId = oauthClientId; + } + + /* @Nullable */ + public String getOauthClientSecret() { + return oauthClientSecret; + } + + public void setOauthClientSecret(/* @Nullable */String oauthClientSecret) { + this.oauthClientSecret = oauthClientSecret; + } + + /* @Nullable */ + public String getVersion() { + return version; + } + + public void setMockPortalRoles(/* @Nullable */Set mockPortalRoles) { + this.mockPortalRoles = mockPortalRoles; + } + + /* @Nullable */ + public Set getMockPortalRoles() { + return mockPortalRoles; + } + + /* @Nullable */ + public String getRestriction() { + return restriction; + } + + public void setRestriction(/* @Nullable */String restriction) { + if (restriction == null) { + this.restriction = DEFAULT_RESTRICTION; + } else { + this.restriction = restriction; + } + } + + /* @Nullable */ + public String getLogLevel() { + return logLevel; + } + + public void setLogLevel(/* @Nullable */String level) { + if (level == null) { + this.logLevel = DEFAULT_LOG_LEVEL.name(); + } else { + this.logLevel = level; + } + BbLogger.instance().setLoggingLevel(LogLevel.valueOf(this.logLevel)); + } + + public void setNewWindow(boolean newWindow) { + this.newWindow = newWindow; + } + + public boolean isNewWindow() { + return newWindow; + } } \ No newline at end of file diff --git a/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/data/WrappedContent.java b/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/data/WrappedContent.java index 9d80ac4..f206af3 100644 --- a/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/data/WrappedContent.java +++ b/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/data/WrappedContent.java @@ -28,6 +28,7 @@ import com.google.common.base.Throwables; import org.apereo.openequella.integration.blackboard.buildingblock.Configuration; import org.apereo.openequella.integration.blackboard.common.BbContext; +import org.apereo.openequella.integration.blackboard.common.BbLogger; import org.apereo.openequella.integration.blackboard.common.BbUtil; import org.apereo.openequella.integration.blackboard.common.PathUtils; import org.apereo.openequella.integration.blackboard.common.content.ContentUtil; @@ -37,561 +38,561 @@ @SuppressWarnings("nls") public class WrappedContent implements Comparable { - private static final BbContext context = BbContext.instance(); - - private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - - private final Id id; - private final WrappedCourse course; - private boolean displayUntil; - private boolean displayAfter; - // Lazy - private Content content; - // Lazy - private WrappedExtendedData extendedData; - - public WrappedContent(String courseId, String contentId) { - this(new WrappedCourse(courseId), contentId); - } - - public WrappedContent(WrappedCourse course, String contentId) { - this(course, BbUtil.getContentId(contentId)); - } - - public WrappedContent(WrappedCourse course, Id contentId) { - this.course = course; - this.id = contentId; - } - - @SuppressWarnings("null") - public WrappedContent(WrappedCourse course, Content content) { - this(course, content.getId()); - this.content = content; - } - - @SuppressWarnings("null") - public WrappedContent(HttpServletRequest request) { - this(request.getParameter(BbUtil.COURSE_ID), request.getParameter(BbUtil.CONTENT_ID)); - } - - public Id getId() { - return id; - } - - public WrappedCourse getCourse() { - return course; - } - - public String getTitle() { - return getExtendedData().getTitle(); - } - - public void setTitle(String title) { - getContent().setTitle(title); - } - - public boolean isAvailable() { - return getContent().getIsAvailable(); - } - - public void setAvailable(boolean available) { - getContent().setIsAvailable(available); - } - - public void setNewWindow(boolean newWindow) { - getContent().setLaunchInNewWindow(newWindow); - } - - public boolean isNewWindow() { - return getContent().getLaunchInNewWindow(); - } - - public Calendar getEndDate() { - return getContent().getEndDate(); - } - - public void setEndDate(Calendar endDate) { - getContent().setEndDate(endDate); - } - - public Calendar getStartDate() { - return getContent().getStartDate(); - } - - public void setStartDate(Calendar startDate) { - getContent().setStartDate(startDate); - } - - public boolean isFolder() { - return getContent().getIsFolder(); - } - - public void setFolder(boolean isFolder) { - getContent().setIsFolder(isFolder); - } - - public boolean getTrackViews() { - return getContent().getIsTracked(); - } - - public void setTrackViews(boolean trackViews) { - getContent().setIsTracked(trackViews); - } - - public boolean isDescribed() { - return getContent().getIsDescribed(); - } - - public void setDescribed(boolean described) { - getContent().setIsDescribed(described); - } - - public boolean isLaunch() { - return getContent().getLaunchInNewWindow(); - } - - public void setLaunch(boolean launch) { - getContent().setLaunchInNewWindow(launch); - } - - public boolean isDisplayAfter() { - return displayAfter; - } - - public void setDisplayAfter(boolean displayAfter) { - this.displayAfter = displayAfter; - } - - public boolean isDisplayUntil() { - return displayUntil; - } - - public void setDisplayUntil(boolean displayUntil) { - this.displayUntil = displayUntil; - } - - public int getPosition() { - return getContent().getPosition(); - } - - public void setPosition(int position) { - getContent().setPosition(position); - } - - public Id getParentId() { - return getContent().getParentId(); - } - - public String getUrl() { - return getExtendedData().getValue(ContentUtil.FIELD_URL); - } - - public void setUrl(String url) { - getExtendedData().setValue(ContentUtil.FIELD_URL, url); - } - - public String getUuid() { - return getExtendedData().getValue(ContentUtil.FIELD_UUID); - } - - public void setUuid(String uuid) { - getExtendedData().setValue(ContentUtil.FIELD_UUID, uuid); - } - - public int getVersion() { - final String versionString = getExtendedData().getValue(ContentUtil.FIELD_VERSION); - if (versionString == null) { - return 0; - } - return Integer.parseInt(versionString); - } - - public void setVersion(int version) { - getExtendedData().setValue(ContentUtil.FIELD_VERSION, Integer.toString(version)); - } - - public String getActivateRequestUuid() { - return getExtendedData().getValue(ContentUtil.FIELD_ACTIVATIONUUID); - } - - public void setActivateRequestUuid(String activateRequestUuid) { - getExtendedData().setValue(ContentUtil.FIELD_ACTIVATIONUUID, activateRequestUuid); - } - - public String getDescription() { - return getExtendedData().getValue(ContentUtil.FIELD_DESCRIPTION); - } - - public void setDescription(String description) { - getExtendedData().setValue(ContentUtil.FIELD_DESCRIPTION, description); - } - - public String getAttachmentName() { - return getExtendedData().getValue(ContentUtil.FIELD_ATTACHMENT_NAME); - } - - public void setAttachmentName(String attachmentName) { - getExtendedData().setValue(ContentUtil.FIELD_ATTACHMENT_NAME, attachmentName); - } - - public String getMimeType() { - return getExtendedData().getValue(ContentUtil.FIELD_MIME_TYPE); - } - - public void setMimeType(String mimeType) { - getExtendedData().setValue(ContentUtil.FIELD_MIME_TYPE, mimeType); - } - - private synchronized WrappedExtendedData getExtendedData() { - if (extendedData == null) { - final Content content = getContent(); - ExtendedData ext = content.getExtendedData(); - boolean wasNull = false; - if (ext == null) { - wasNull = true; - ext = new ExtendedData(); - content.setExtendedData(ext); - } - extendedData = new WrappedExtendedData(ext, wasNull); - } - return extendedData; - } - - /** - * The HTML to display in the content's summary - * - * @param request - * @param bbEvaluateURLs - * Use for modify.jsp and modify_proc.jsp. modify.jsp *used* to replace - * the BB placeholders. Don't know what's happened there. - */ - public String getHtml(HttpServletRequest request, boolean bbEvaluateURLs) { - String html = ItemUtil.getHtml(Configuration.instance().getEquellaUrl(), getUrl(), getAttachmentName(), - getMimeType(), getTitle(), getDescription(), isNewWindow()); - if (bbEvaluateURLs) { - if (request != null && bbEvaluateURLs) { - final BbSession bbSession = BbSessionManagerServiceExFactory.getInstance().getSession(request); - html = blackboardEscape(request, bbSession, html); - } - } - return html; - } - - public void startSelectionSession(HttpServletRequest request, HttpServletResponse response) throws Exception { - // if( wrapSelectionSession ) - // { - // ContentServlet.forwardToContentWrapper(request, response, "AddContentBody"); - // } - // else - // { - final StringBuilder resourceHref = new StringBuilder( - PathUtils.urlPath(BbUtil.getBlockRelativePath(), "AddContentBody")); - // Foward our params - final String queryString = request.getQueryString(); - if (!Strings.isNullOrEmpty(queryString)) { - resourceHref.append("?").append(queryString); - // To make gradebook work. Pass on the content id to the inner iframe URL - if (!queryString.contains(CONTENT_ID)) { - resourceHref.append("&" + CONTENT_ID + "=").append(URLEncoder.encode(getId().toExternalString(), "UTF-8")); - } - } - response.sendRedirect(resourceHref.toString()); - // } - } - - /** - * Note: does not use Blackboard's sepecial placeholders for courseId, contentId - * - * @return - */ - public String getParameterString() { - return COURSE_ID + "=" + course.getId().toExternalString() + "&" + CONTENT_ID + "=" + id.toExternalString(); - } - - @Override - public int compareTo(WrappedContent content) { - int diff = getPosition() - content.getPosition(); - if (diff == 0) { - diff = -1; - } - return diff; - } - - public String getBaseParameters(HttpServletRequest request) { - return blackboardEscape(request, null, BbUtil.getParamBase()); - } - - private String blackboardEscape(HttpServletRequest request, BbSession bbSession, String text) { - if (request != null) { - if (bbSession == null) { - bbSession = BbSessionManagerServiceExFactory.getInstance().getSession(request); - } - - try { - return bbSession.encodeTemplateUrl(request, text); - } catch (final InitializationException e) { - // don't care, not as if we can do anything about it - } catch (final PersistenceException e) { - // ditto - } - } - return text; - } - - public String getReferrer(boolean modify) throws Exception { - if (modify) { - return PlugInUtil.getEditableContentReturnURL(content.getId(), course.getCourse().getId()); - } else { - return PlugInUtil.getDisplayContentReturnURL(content.getId(), course.getCourse().getId()); - } - } - - private ContentDbLoader getLoader() throws Exception { - return (ContentDbLoader) context.getPersistenceManager().getLoader(ContentDbLoader.TYPE); - } - - public void load(HttpServletRequest request) throws Exception { - if (content == null) { - content = getLoader().loadById(BbUtil.getContentId(request.getParameter("content_id"))); - } - - final Calendar endDate = getEndDate(); - displayUntil = endDate != null; - if (!displayUntil) { - setEndDate(Calendar.getInstance()); - } - - final Calendar startDate = getStartDate(); - displayAfter = startDate != null; - if (!displayAfter) { - setStartDate(Calendar.getInstance()); - } - } - - public void persist(HttpServletRequest request) throws Exception { - BbUtil.trace("persist(request)"); - final ClassLoader oldLoader = Thread.currentThread().getContextClassLoader(); - try { - Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); - boolean isNew = false; - if (content == null) { - BbUtil.trace("content shouldn't be null here..."); - isNew = true; - content = new CourseDocument(); - content.setPosition(-1); - content.setContentHandler(BbUtil.CONTENT_HANDLER); - } - if (!displayAfter) { - BbUtil.trace("set start date to null"); - content.setStartDate(null); - } - if (!displayUntil) { - BbUtil.trace("set end date to null"); - content.setEndDate(null); - } - - BbUtil.trace("Getting config"); - final Configuration configuration = Configuration.instance(); - final String equellaUrl = configuration.getEquellaUrl(); - BbUtil.trace("EQUELLA URL is " + equellaUrl); - BbUtil.trace("Calling persistContent"); - ContentUtil.instance().persistContent(content, getCourse().getCourse(), equellaUrl, isNew); - } catch (Exception e) { - BbUtil.error("Error persisting content", e); - throw Throwables.propagate(e); - } finally { - Thread.currentThread().setContextClassLoader(oldLoader); - } - } - - public void remove(HttpServletRequest request) throws Exception { - BbUtil.debug("remove(request)"); - final WrappedUser user = WrappedUser.getUser(request); - try { - BbUtil.trace("Getting config"); - final Configuration config = Configuration.instance(); - final String equellaUrl = config.getEquellaUrl(); - BbUtil.trace("EQUELLA URL is " + equellaUrl); - BbUtil.trace("Calling removeContent"); - ContentUtil.instance().removeContent(getContent(), getCourse().getCourse(), equellaUrl, user.getToken()); - } finally { - user.clearContext(); - } - } - - public void modify(HttpServletRequest request) throws Exception { - BbUtil.trace("modify(request)"); - try { - final String name = request.getParameter("name"); - final String description = request.getParameter("description"); - final String available = request.getParameter("available"); - final String views = request.getParameter("views"); - final String described = request.getParameter("described"); - final String startDateString = request.getParameter("date_start_datetime"); - final String endDateString = request.getParameter("date_end_datetime"); - final String displayAfterString = request.getParameter("date_start_checkbox"); - final String displayUntilString = request.getParameter("date_end_checkbox"); - final String newWindowString = request.getParameter("newWindow"); - BbUtil.trace("name=" + name); - BbUtil.trace("description=" + description); - BbUtil.trace("available=" + available); - BbUtil.trace("views=" + views); - BbUtil.trace("described=" + described); - BbUtil.trace("startDateString=" + startDateString); - BbUtil.trace("endDateString=" + endDateString); - BbUtil.trace("displayAfterString=" + displayAfterString); - BbUtil.trace("displayUntilString=" + displayUntilString); - BbUtil.trace("newWindowString=" + newWindowString); - - // Kill legacy shiite. - killLegacyFields(); - - setTitle(name); - setDescription(description); - setAvailable(available != null && available.equals("true")); - setTrackViews(views != null && views.equals("true")); - setDescribed(described != null && described.equals("true")); - setDisplayAfter(displayAfterString != null && displayAfterString.equals("1")); - setDisplayUntil(displayUntilString != null && displayUntilString.equals("1")); - setNewWindow("true".equals(newWindowString)); - if (!Strings.isNullOrEmpty(startDateString)) { - final Calendar startDate = Calendar.getInstance(); - startDate.setTime(DATE_FORMAT.parse(startDateString)); - setStartDate(startDate); - } - if (!Strings.isNullOrEmpty(endDateString)) { - final Calendar endDate = Calendar.getInstance(); - endDate.setTime(DATE_FORMAT.parse(endDateString)); - setEndDate(endDate); - } - } catch (Exception e) { - BbUtil.error("Error modifying content", e); - throw Throwables.propagate(e); - } - } - - /** - * Removes the commented out XML bollocks and puts into Extended Data - */ - private void killLegacyFields() { - final Content c = getContent(); - final String body = c.getBody().getFormattedText(); - if (body.length() == 0) { - return; - } - if (body.startsWith(" - + @@ -67,7 +67,7 @@ - + @@ -76,19 +76,19 @@ - + - + - + - + diff --git a/oeqPrimaryB2/src/main/properties/version-unresolved.properties b/oeqPrimaryB2/src/main/properties/version-unresolved.properties new file mode 100644 index 0000000..c077123 --- /dev/null +++ b/oeqPrimaryB2/src/main/properties/version-unresolved.properties @@ -0,0 +1,2 @@ +# Used to display the build version of the building block in Blackboard. +version.b2=@VERSION@ \ No newline at end of file diff --git a/oeqPrimaryB2/src/main/webapp/admin/config.jsp b/oeqPrimaryB2/src/main/webapp/admin/config.jsp index 89bdfa0..a4c299e 100644 --- a/oeqPrimaryB2/src/main/webapp/admin/config.jsp +++ b/oeqPrimaryB2/src/main/webapp/admin/config.jsp @@ -14,6 +14,7 @@ <%@page import="org.apereo.openequella.integration.blackboard.buildingblock.data.WrappedUser"%> <%@page import="org.apereo.openequella.integration.blackboard.buildingblock.Configuration"%> <%@page import="org.apereo.openequella.integration.blackboard.common.BbUtil"%> +<%@page import="org.apereo.openequella.integration.blackboard.common.BbLogger"%> <%@ taglib uri="/bbNG" prefix="bbng"%> <%@ taglib uri="/tle" prefix="tle"%> @@ -90,12 +91,14 @@ String clientSecret = configuration.getOauthClientSecret(); String secretId = configuration.getSecretId(); String secret = configuration.getSecret(); String restriction = configuration.getRestriction(); +String logLevel = configuration.getLogLevel(); boolean newWindow = configuration.isNewWindow(); +String loggingDetails = BbLogger.instance().getLoggingDetails(); String title = "EQUELLA Server Configuration"; int number = 1; %> - + @@ -142,25 +145,40 @@ int number = 1; - - - "/> - "/> - "/> - "/> - - - - - - - - - + + + "/> + "/> + "/> + "/> + + + + + + "/> + "/> + "/> + "/> + "/> + "/> + + + + + + + + +
- <%=version%> -
+ <%=version%> + + +
+ Logging: <%=loggingDetails%> +
diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/SynchroniseContentThread.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/SynchroniseContentThread.java index ec4403e..5d35704 100644 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/SynchroniseContentThread.java +++ b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/SynchroniseContentThread.java @@ -11,6 +11,7 @@ import blackboard.persist.content.ContentDbLoader; import org.apereo.openequella.integration.blackboard.common.BbContext; +import org.apereo.openequella.integration.blackboard.common.BbLogger; import org.apereo.openequella.integration.blackboard.common.BbUtil; import org.apereo.openequella.integration.blackboard.common.content.ContentUtil; import org.apereo.openequella.integration.blackboard.common.content.ContentUtil.ContentRegisteredResponse; @@ -22,161 +23,166 @@ * @author Aaron */ public class SynchroniseContentThread extends Thread { - private final String institutionUrl; - private final boolean available; - - SynchroniseContentThread(String institutionUrl, boolean available) { - this.institutionUrl = institutionUrl; - this.available = available; - } - - private void recurseChildren(ContentDbLoader contentDbLoader, Content content, List equellaContents) - throws Exception { - if (content.getIsFolder()) { - BbUtil.debug("Content \"" + content.getTitle() + "\" is a folder. Recursively getting contents."); - for (Content courseContent : contentDbLoader.loadChildren(content.getId())) { - recurseChildren(contentDbLoader, courseContent, equellaContents); - } - } else { - BbUtil.debug("Content \"" + content.getTitle() + "\" is content. Checking EQUELLA-ness."); - - // check for EQUELLA-ness - String handler = content.getContentHandler(); - if (handler.equals(BbUtil.CONTENT_HANDLER) || handler.equals("resource/tle-myitem") - || handler.equals("resource/tle-plan")) { - BbUtil.debug("Content \"" + content.getTitle() + "\" has a matching handler."); - equellaContents.add(content); - - // we need to see if it belongs to this institution (NO CAN - // DO??)... - /* - * final Matcher equellaMatcher = getEquellaUrlMatcher(institutionUrl, content - * .getBody().getText()); if( equellaMatcher.find() ) { - * BbUtil.debug("URL matched! Adding to list."); equellaContents.add(content); - * } else { BbUtil.debug("No URL match."); } - */ - } - } - } - - @Override - public void run() { - // scan every course, look at every content and see if it's in the - // database, if it's not then add it - final BbPersistenceManager pm = BbContext.instance().getPersistenceManager(); - try { - final ContentDbLoader contentDbLoader = (ContentDbLoader) pm.getLoader(ContentDbLoader.TYPE); - - for (blackboard.data.course.Course course : WebServiceUtil.getBbCourses(null)) { - final List equellaContents = new ArrayList(); - final boolean courseAvailable = course.getIsAvailable(); - // if( available && !course.getIsAvailable() ) - // { - // debug("Course " + course.getTitle() + - // " is not Available, skipping"); - // continue; - // } - - final Id courseId = course.getId(); - BbUtil.debug("Looking at course " + courseId.toExternalString()); - - for (Content folder : BbUtil.getBbFoldersForCourse(courseId)) { - BbUtil.debug("Found folder directly under course: " + folder.getTitle()); - recurseChildren(contentDbLoader, folder, equellaContents); - } - - final Set itemContent = new HashSet(); - BbUtil.debug("Found " + equellaContents.size() + " EQUELLA contents"); - for (Content equellaContent : equellaContents) { - final Id folderId = equellaContent.getParentId(); - - // BbUtil.debug("Getting properties for " - // + equellaContent.getId().toExternalString() + " " - // + equellaContent.getTitle()); - ItemInfo itemInfo = null; - try { - itemInfo = ContentUtil.instance().ensureProperties(equellaContent, course, folderId, institutionUrl); - - } catch (Exception t) { - // ignore - } - - // In freak cases it can return null - if (itemInfo != null) { - final ItemKey itemKey = itemInfo.getItemKey(); - - // check the DB for it, if not then insert - ContentRegisteredResponse reg = RegistrationUtil.contentIsRegistered(itemKey); - if (!reg.isRegistered()) { - BbUtil.debug("Content \"" + itemKey + "\" not in DB so recording it"); - // EquellaContentUtil.recordItem(itemInfo.getItemKey(), - // true, - // equellaContent.getIsAvailable(), courseAvailable, - // equellaContent - // .getTitle(), - // equellaContent.getCreatedDate().getTime(), - // equellaContent.getModifiedDate().getTime(), - // course.getTitle()); - } else - // if( reg.isAvailable() != course.getIsAvailable() ) - { - // BbUtil.debug("Content is marked as available=" - // + reg.isAvailable() + " but course is available=" - // + courseAvailable - // + ". Updating entry."); - // EquellaContentUtil.recordItem(itemInfo.getItemKey(), - // false, - // equellaContent.getIsAvailable(), courseAvailable, - // equellaContent - // .getTitle(), - // equellaContent.getCreatedDate().getTime(), - // equellaContent.getModifiedDate().getTime(), - // course.getTitle()); - BbUtil.debug("Content \"" + itemKey + "\" already in DB, updating record in case of changes"); - } - // else - // { - // BbUtil.debug("Content \"" + itemKey - // + "\" is already registered in DB"); - // } - - // Need to extract the current description (the XML is - // kept up to date) - final String description = itemInfo.getDescription(); - - RegistrationUtil.recordItem(itemInfo.getItemKey(), !reg.isRegistered(), equellaContent.getIsAvailable(), - courseAvailable, equellaContent.getTitle(), description, itemInfo.getAttachmentName(), - equellaContent.getCreatedDate().getTime(), equellaContent.getModifiedDate().getTime(), - course.getTitle()); - - itemContent.add(itemKey); - } - } - - // check DB rows for this institutionUrl and course, whatever - // not found in contents should be chucked - final List dbItems = RegistrationUtil.findEquellaContentByCourse(institutionUrl, - courseId.toExternalString(), available); - BbUtil.debug("Found " + dbItems.size() + " registered content. Cross referencing it with discovered content."); - for (ItemKey dbItem : dbItems) { - if (!itemContent.contains(dbItem)) { - BbUtil.debug("Content \"" + dbItem + "\" in DB but not found in course contents. Removing from DB."); - RegistrationUtil.unrecordItem(dbItem.getDatabaseId(), dbItem.getContentId()); - } - } - } - - // Cleanup NULL contentid columns. These should no longer exist. - BbUtil.debug("Removing registered content with bad data."); - int removed = RegistrationUtil.cleanupBadContent(institutionUrl); - if (removed > 0) { - BbUtil.debug("Removed " + removed + " entries with bad data."); - } else { - BbUtil.debug("No bad data exists."); - } - } catch (Exception e) { - BbUtil.error("Exception happened", e); - throw new RuntimeException(e); - } - } + private final String institutionUrl; + private final boolean available; + + SynchroniseContentThread(String institutionUrl, boolean available) { + this.institutionUrl = institutionUrl; + this.available = available; + } + + private void recurseChildren(ContentDbLoader contentDbLoader, Content content, List equellaContents) + throws Exception { + if (content.getIsFolder()) { + BbLogger.instance() + .logDebug("Content \"" + content.getTitle() + "\" is a folder. Recursively getting contents."); + for (Content courseContent : contentDbLoader.loadChildren(content.getId())) { + recurseChildren(contentDbLoader, courseContent, equellaContents); + } + } else { + BbLogger.instance().logDebug("Content \"" + content.getTitle() + "\" is content. Checking EQUELLA-ness."); + + // check for EQUELLA-ness + String handler = content.getContentHandler(); + if (handler.equals(BbUtil.CONTENT_HANDLER) || handler.equals("resource/tle-myitem") + || handler.equals("resource/tle-plan")) { + BbLogger.instance().logDebug("Content \"" + content.getTitle() + "\" has a matching handler."); + equellaContents.add(content); + + // we need to see if it belongs to this institution (NO CAN + // DO??)... + /* + * final Matcher equellaMatcher = getEquellaUrlMatcher(institutionUrl, content + * .getBody().getText()); if( equellaMatcher.find() ) { + * BbLogger.instance().logDebug("URL matched! Adding to list."); + * equellaContents.add(content); } else { + * BbLogger.instance().logDebug("No URL match."); } + */ + } + } + } + + @Override + public void run() { + // scan every course, look at every content and see if it's in the + // database, if it's not then add it + final BbPersistenceManager pm = BbContext.instance().getPersistenceManager(); + try { + final ContentDbLoader contentDbLoader = (ContentDbLoader) pm.getLoader(ContentDbLoader.TYPE); + + for (blackboard.data.course.Course course : WebServiceUtil.getBbCourses(null)) { + final List equellaContents = new ArrayList(); + final boolean courseAvailable = course.getIsAvailable(); + // if( available && !course.getIsAvailable() ) + // { + // debug("Course " + course.getTitle() + + // " is not Available, skipping"); + // continue; + // } + + final Id courseId = course.getId(); + BbLogger.instance().logDebug("Looking at course " + courseId.toExternalString()); + + for (Content folder : BbUtil.getBbFoldersForCourse(courseId)) { + BbLogger.instance().logDebug("Found folder directly under course: " + folder.getTitle()); + recurseChildren(contentDbLoader, folder, equellaContents); + } + + final Set itemContent = new HashSet(); + BbLogger.instance().logDebug("Found " + equellaContents.size() + " EQUELLA contents"); + for (Content equellaContent : equellaContents) { + final Id folderId = equellaContent.getParentId(); + + // BbLogger.instance().logDebug("Getting properties for " + // + equellaContent.getId().toExternalString() + " " + // + equellaContent.getTitle()); + ItemInfo itemInfo = null; + try { + itemInfo = ContentUtil.instance().ensureProperties(equellaContent, course, folderId, institutionUrl); + + } catch (Exception t) { + // ignore + } + + // In freak cases it can return null + if (itemInfo != null) { + final ItemKey itemKey = itemInfo.getItemKey(); + + // check the DB for it, if not then insert + ContentRegisteredResponse reg = RegistrationUtil.contentIsRegistered(itemKey); + if (!reg.isRegistered()) { + BbLogger.instance().logDebug("Content \"" + itemKey + "\" not in DB so recording it"); + // EquellaContentUtil.recordItem(itemInfo.getItemKey(), + // true, + // equellaContent.getIsAvailable(), courseAvailable, + // equellaContent + // .getTitle(), + // equellaContent.getCreatedDate().getTime(), + // equellaContent.getModifiedDate().getTime(), + // course.getTitle()); + } else + // if( reg.isAvailable() != course.getIsAvailable() ) + { + // BbLogger.instance().logDebug("Content is marked as available=" + // + reg.isAvailable() + " but course is available=" + // + courseAvailable + // + ". Updating entry."); + // EquellaContentUtil.recordItem(itemInfo.getItemKey(), + // false, + // equellaContent.getIsAvailable(), courseAvailable, + // equellaContent + // .getTitle(), + // equellaContent.getCreatedDate().getTime(), + // equellaContent.getModifiedDate().getTime(), + // course.getTitle()); + BbLogger.instance() + .logDebug("Content \"" + itemKey + "\" already in DB, updating record in case of changes"); + } + // else + // { + // BbLogger.instance().logDebug("Content \"" + itemKey + // + "\" is already registered in DB"); + // } + + // Need to extract the current description (the XML is + // kept up to date) + final String description = itemInfo.getDescription(); + + RegistrationUtil.recordItem(itemInfo.getItemKey(), !reg.isRegistered(), equellaContent.getIsAvailable(), + courseAvailable, equellaContent.getTitle(), description, itemInfo.getAttachmentName(), + equellaContent.getCreatedDate().getTime(), equellaContent.getModifiedDate().getTime(), + course.getTitle()); + + itemContent.add(itemKey); + } + } + + // check DB rows for this institutionUrl and course, whatever + // not found in contents should be chucked + final List dbItems = RegistrationUtil.findEquellaContentByCourse(institutionUrl, + courseId.toExternalString(), available); + BbLogger.instance().logDebug( + "Found " + dbItems.size() + " registered content. Cross referencing it with discovered content."); + for (ItemKey dbItem : dbItems) { + if (!itemContent.contains(dbItem)) { + BbLogger.instance() + .logDebug("Content \"" + dbItem + "\" in DB but not found in course contents. Removing from DB."); + RegistrationUtil.unrecordItem(dbItem.getDatabaseId(), dbItem.getContentId()); + } + } + } + + // Cleanup NULL contentid columns. These should no longer exist. + BbLogger.instance().logDebug("Removing registered content with bad data."); + int removed = RegistrationUtil.cleanupBadContent(institutionUrl); + if (removed > 0) { + BbLogger.instance().logDebug("Removed " + removed + " entries with bad data."); + } else { + BbLogger.instance().logDebug("No bad data exists."); + } + } catch (Exception e) { + BbLogger.instance().logError("Exception happened", e); + throw new RuntimeException(e); + } + } } \ No newline at end of file From e016b780ce39c5f8d9ed3e1f30820fc08bced260 Mon Sep 17 00:00:00 2001 From: C Beach Date: Fri, 2 Nov 2018 12:44:31 -0500 Subject: [PATCH 02/14] #4 Troubleshooting efforts --- TESTING.md | 2 + build.gradle | 3 +- .../blackboard/oeqAudit/Audit.java | 113 +- .../main/manifests/bb-manifest-unresolved.xml | 2 +- oeqAuditB2/src/main/webapp/admin/config.jsp | 49 +- oeqAuditB2/src/main/webapp/taglibs/bbData.tld | 182 +- oeqAuditB2/src/main/webapp/taglibs/bbUI.tld | 3648 ++++++++--------- oeqAuditB2/src/main/webapp/taglibs/bbUI.xsl | 64 +- .../blackboard/common/BbLogger.java | 28 +- .../integration/blackboard/common/BbUtil.java | 4 +- .../buildingblock/Configuration.java | 1216 +++--- .../buildingblock/data/WrappedContent.java | 1 - .../buildingblock/data/WrappedUser.java | 14 +- .../lti/FixedBasicLtiLauncher.java | 310 +- .../buildingblock/servlet/ContentServlet.java | 60 +- .../main/manifests/bb-manifest-unresolved.xml | 18 +- oeqPrimaryB2/src/main/webapp/WEB-INF/web.xml | 8 +- .../main/webapp/contribute/modify_proc.jsp | 4 +- .../src/main/manifests/bb-manifest.xml | 10 +- 19 files changed, 2935 insertions(+), 2801 deletions(-) diff --git a/TESTING.md b/TESTING.md index 98936c7..764ff75 100644 --- a/TESTING.md +++ b/TESTING.md @@ -16,6 +16,8 @@ Ensure the version on the settings page is the same version as in the war filena ## Access Existing Integration Link +## Edit Integration Link + ## Add openEQUELLA Module ## Course Tools > openEQUELLA Contribute diff --git a/build.gradle b/build.gradle index 0aeffde..606e98a 100644 --- a/build.gradle +++ b/build.gradle @@ -68,7 +68,8 @@ allprojects { ( !file.name.startsWith('jetty-') && !file.name.startsWith('geronimo-') && - !file.name.startsWith('spring-') + !file.name.startsWith('spring-') && + !file.name.startsWith('serializer-') ) } diff --git a/oeqAuditB2/src/main/java/org/apereo/openequella/integration/blackboard/oeqAudit/Audit.java b/oeqAuditB2/src/main/java/org/apereo/openequella/integration/blackboard/oeqAudit/Audit.java index 7b684d5..93c0e9a 100644 --- a/oeqAuditB2/src/main/java/org/apereo/openequella/integration/blackboard/oeqAudit/Audit.java +++ b/oeqAuditB2/src/main/java/org/apereo/openequella/integration/blackboard/oeqAudit/Audit.java @@ -1,42 +1,105 @@ package org.apereo.openequella.integration.blackboard.oeqAudit; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.sql.ResultSet; -import java.sql.SQLException; import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; import java.util.Date; +import java.util.List; //import javax.servlet.http.HttpServletRequest; import blackboard.platform.BbServiceManager; -import javax.servlet.http.HttpServletRequest; public class Audit { - private volatile static Audit instance; - protected Audit() { + public static void main(String[] args) { + + } + + public static List auditClasses() { + List audit = new ArrayList<>(); + try { + audit.add("Available: " + blackboard.data.blti.BasicLTIDomainConfig.class.getName()); + } catch (Exception e) { + audit.add("MISSING: blackboard.data.blti.BasicLTIDomainConfig"); + } + + try { + audit.add("Available: " + blackboard.platform.blti.BasicLTIDomainConfigManager.class.getName()); + } catch (Exception e) { + audit.add("MISSING: blackboard.platform.blti.BasicLTIDomainConfigManager"); + } + + try { + audit.add("Available: " + blackboard.data.blti.BasicLTIPlacement.class.getName()); + } catch (Exception e) { + audit.add("MISSING: blackboard.data.blti.BasicLTIPlacement"); + } + + try { + audit.add("Available: " + blackboard.data.blti.BasicLTIDomainHost.class.getName()); + } catch (Exception e) { + audit.add("MISSING: blackboard.data.blti.BasicLTIDomainHost"); + } + + // "blackboard.data.blti.BasicLTIDomainConfig.SendUserData", + // "blackboard.data.blti.BasicLTIDomainConfig.Status", + // "blackboard.data.blti.BasicLTIPlacement.Type", + // "blackboard.data.gradebook.impl.OutcomeDefinition", + // "blackboard.db.BbDatabase", "blackboard.db.ConnectionManager", + // "blackboard.db.DbUtil", + // "blackboard.persist.gradebook.impl.OutcomeDefinitionDbLoader", + // "blackboard.persist.impl.SimpleSelectQuery", + // "blackboard.platform.blti.BasicLTIDomainConfigManager", + // "blackboard.platform.blti.BasicLTIDomainConfigManagerFactory", + // "blackboard.platform.blti.BasicLTIPlacementManager", + // "blackboard.platform.ContentWrapperHelper", + // "blackboard.platform.gradebook2.GradableItem", + // "blackboard.platform.gradebook2.GradableItemManager", + // "blackboard.platform.gradebook2.GradebookManagerFactory", + // "blackboard.platform.gradebook2.impl.GradableItemDAO", + // "blackboard.platform.gradebook2.impl.ScoreProviderDAO", + // "blackboard.platform.gradebook2.ScoreProvider", + // "blackboard.platform.plugin.ContentHandler", + // "blackboard.platform.plugin.ContentHandlerDbLoader", + // "blackboard.platform.plugin.ContentHandlerDbPersister", + // "blackboard.platform.plugin.ContentHandlerType.ActionType", + // "blackboard.platform.plugin.PlugInException", + // "blackboard.platform.query.Criteria", + // "blackboard.platform.query.CriterionBuilder", + // "blackboard.platform.security.AccessManagerService", + // "blackboard.platform.session.BbSessionManagerServiceExFactory", + // "blackboard.platform.vxi.data.VirtualHost", + // "blackboard.platform.vxi.data.VirtualInstallation", + // "blackboard.platform.vxi.service.VirtualInstallationManager", + // "blackboard.platform.ws.AxisHelpers", "blackboard.platform.ws.SessionVO", + // "blackboard.platform.ws.WebserviceContext", + // "blackboard.platform.ws.WebserviceException", + // "blackboard.platform.ws.WebserviceLogger" }; + + return audit; + } + + private volatile static Audit instance; + + protected Audit() { - } + } - public static Audit instance() { - if (instance != null) { - return instance; - } + public static Audit instance() { + if (instance != null) { + return instance; + } - instance = new Audit(); - return instance; - } + instance = new Audit(); + return instance; + } - public String getAudit() { - // Test to pull in the dep. - String state = BbServiceManager.getState().name(); - return "A successful test on " + (new Date()) + " State: " + state; - } + public String getAudit() { + // Test to pull in the dep. + String state = BbServiceManager.getState().name(); + return "A successful test on " + (new Date()) + " State: " + state; + } - public String getError() { - return null;// "An error msg here"; - } + public String getError() { + return null;// "An error msg here"; + } } diff --git a/oeqAuditB2/src/main/manifests/bb-manifest-unresolved.xml b/oeqAuditB2/src/main/manifests/bb-manifest-unresolved.xml index f5b436c..bdcf11f 100644 --- a/oeqAuditB2/src/main/manifests/bb-manifest-unresolved.xml +++ b/oeqAuditB2/src/main/manifests/bb-manifest-unresolved.xml @@ -1,7 +1,7 @@ - + diff --git a/oeqAuditB2/src/main/webapp/admin/config.jsp b/oeqAuditB2/src/main/webapp/admin/config.jsp index d0f41ca..184d5ef 100644 --- a/oeqAuditB2/src/main/webapp/admin/config.jsp +++ b/oeqAuditB2/src/main/webapp/admin/config.jsp @@ -4,21 +4,21 @@ import="java.util.*, java.text.*, java.io.*, - org.apereo.openequella.integration.blackboard.b2auditor.Auditor"%> + org.apereo.openequella.integration.blackboard.oeqAudit.Audit"%> <%@ taglib uri="/bbUI" prefix="ui"%> <% - Auditor auditor = Auditor.instance(); + Audit audit = Audit.instance(); String ex = null; String message = null; try { - message = auditor.getAudit(); + message = audit.getAudit(); } catch( Exception e ) { @@ -27,12 +27,14 @@ e.printStackTrace(w); ex = s.toString(); } - String error = (auditor.getError() == null ? ex : auditor.getError()); + String error = (audit.getError() == null ? ex : audit.getError()); - String title = "openEQUELLA B2 Auditor Plugin"; + String title = "openEQUELLA B2 Audit Plugin"; + + List auditForClasses = audit.auditClasses(); %> - + <%=title%> @@ -46,15 +48,28 @@
<%=error%>
- <% } - if(message != null) - { - %> - -
- <%=message%> -
-
- - <% } %> + <% } + if(message != null) + { + %> + +
+ <%=message%> +
+
+ + <% } + if(audit != null) + { + for(String auditBit : auditForClasses) { + %> +
+ <%=auditBit%> +
+ + + <% } }%> + + +
\ No newline at end of file diff --git a/oeqAuditB2/src/main/webapp/taglibs/bbData.tld b/oeqAuditB2/src/main/webapp/taglibs/bbData.tld index 6b69013..a5085cd 100644 --- a/oeqAuditB2/src/main/webapp/taglibs/bbData.tld +++ b/oeqAuditB2/src/main/webapp/taglibs/bbData.tld @@ -1,91 +1,91 @@ - - - - - 1.0 - - 1.1 - - Blackboard Data Tag Library - - /bbData - - The Blackboard Data tag library encapsulates interaction - with the Blackboard data and logic layer to allow separation of - the UI elements (bbUI) and other page logic. - - - context - - blackboard.servlet.tags.data.ContextTag - - blackboard.servlet.tags.data.ContextTei - JSP - - In order to support forward compatibility for 5.5 - extension into 6, we need to build a hook that will allow us to - capture virtual installation info in 6. Programmatically, this - was going to be through the Context object. We need to - formalize the interface in 5.5 so that developers can use the - API to ensure 6.0 conformance. As a high-level wrapper, we also - need a taglib for simple inclusion. The taglib would be a - no-arg tag required before any service APIs were called, with a - required closing tag at the bottom of the page (ensuring - context is released). API calls would be Context.setInstance( - HttpServletRequest req ) and Context.releaseInstance(). We need - to include some kind of integrity checking/reference counting - to ensure proper behavior of nested calls. - - id - may be used to explicitly reference the context object elsewhere - in the page (also can access the context from the Java APIs, as - usual) - - license - license key, used to ensure that page will not display if - the license is not valid - - navItem - initializes a NavigationItemControl that will automatically - handle entitlements, and also sets default values for the page title - and breadcrumbs on a given page. - entitlements - a String[] containing entitlements to check before displaying the page - entitlements as per - blackboard.platform.security.AccessManagerService.userHasAnyEntitlements() - entitlement - a String containing a single entitlement to check before displaying the page - entitlement as per - blackboard.platform.security.AccessManagerService.userEntitlement() - - - - - id - false - true - - - license - false - true - - - navItem - false - true - - - - entitlements - false - true - - - - entitlement - false - true - - - - - - + + + + + 1.0 + + 1.1 + + Blackboard Data Tag Library + + /bbData + + The Blackboard Data tag library encapsulates interaction + with the Blackboard data and logic layer to allow separation of + the UI elements (bbUI) and other page logic. + + + context + + blackboard.servlet.tags.data.ContextTag + + blackboard.servlet.tags.data.ContextTei + JSP + + In order to support forward compatibility for 5.5 + extension into 6, we need to build a hook that will allow us to + capture virtual installation info in 6. Programmatically, this + was going to be through the Context object. We need to + formalize the interface in 5.5 so that developers can use the + API to ensure 6.0 conformance. As a high-level wrapper, we also + need a taglib for simple inclusion. The taglib would be a + no-arg tag required before any service APIs were called, with a + required closing tag at the bottom of the page (ensuring + context is released). API calls would be Context.setInstance( + HttpServletRequest req ) and Context.releaseInstance(). We need + to include some kind of integrity checking/reference counting + to ensure proper behavior of nested calls. + + id - may be used to explicitly reference the context object elsewhere + in the page (also can access the context from the Java APIs, as + usual) + + license - license key, used to ensure that page will not display if + the license is not valid + + navItem - initializes a NavigationItemControl that will automatically + handle entitlements, and also sets default values for the page title + and breadcrumbs on a given page. + entitlements - a String[] containing entitlements to check before displaying the page + entitlements as per + blackboard.platform.security.AccessManagerService.userHasAnyEntitlements() + entitlement - a String containing a single entitlement to check before displaying the page + entitlement as per + blackboard.platform.security.AccessManagerService.userEntitlement() + + + + + id + false + true + + + license + false + true + + + navItem + false + true + + + + entitlements + false + true + + + + entitlement + false + true + + + + + + diff --git a/oeqAuditB2/src/main/webapp/taglibs/bbUI.tld b/oeqAuditB2/src/main/webapp/taglibs/bbUI.tld index 03fe692..a57792f 100644 --- a/oeqAuditB2/src/main/webapp/taglibs/bbUI.tld +++ b/oeqAuditB2/src/main/webapp/taglibs/bbUI.tld @@ -1,1824 +1,1824 @@ - - - - - - - - - 1.0 - 1.1 - Blackboard UI Tag Library - /bbUI - - The Blackboard User Interface tag library encapsulates Blackboard user - interface elements to allow developers easy access to the Blackboard look and feel. - - - - loginWelcome - blackboard.servlet.tags.LoginWelcomeTag - empty - - Creates the standard login welcome panel. - - - errorMessage - false - true - - - - - - loginForm - blackboard.servlet.tags.LoginFormTag - empty - - Creates the standard login form ( same look and feel as rendered by login.pl ). - - validateFormScript - name of the inline script which will validate the login form. - errorMessage - error message from previous login attempt - - - validateFormScript - false - true - - - errorMessage - false - true - - - target - false - true - - - - - createAccountForm - blackboard.servlet.tags.CreateAccountFormTag - empty - - Creates the create-account form. - - createAccountLink - name of the page that will create a new user account. - - - createAccountLink - false - true - - - - - copyright - blackboard.servlet.tags.CopyrightTag - empty - - Standard HTML snippet for Blackboard copyright notice. - - - - - validateLoginFormScript - blackboard.servlet.tags.ValidateLoginFormScriptTag - empty - - Validates login form. - - - - - inlineScript - blackboard.servlet.tags.InlineScriptTag - JSP - - Inserts inline script into HTML. - - scriptLanguage - assumed to be JavaScript, if no input is provided. - scriptRef1 - reference to existing script. - scriptRef2 - reference to another existing script. - scriptRef3 - reference to another existing script. - scriptRef4 - reference to another existing script. - - - scriptLanguage - false - true - - - scriptRef1 - false - true - - - scriptRef2 - false - true - - - scriptRef3 - false - true - - - scriptRef4 - false - true - - - - - spacer - blackboard.servlet.tags.SpacerTag - empty - - Creates an invisible area on the page with the specified height - and width. - - height - Vertical size of the spacer in pixels [1]; - width - Horizontal size of the spacer in pixels [1]; - - - height - false - true - - - width - false - true - - - align - false - true - - - - - devDocTemplate - blackboard.servlet.tags.DocumentTemplateTag - JSP - - combines the docTemplateHead and docTemplateBody tag (for use - when you don't need to add items to the HEAD of the HTML doc. - - - title - true - true - - - - - docTemplate - blackboard.servlet.tags.ProductionDocTemplateTag - JSP - combines the docTemplateHead and docTemplateBody tag (for use - when you don't need to add items to the HEAD of the HTML doc. - - - - title - false - true - - - onLoad - false - true - - - onUnload - false - true - - - onSubmit - false - true - - - - - docTemplateHead - blackboard.servlet.tags.ProductionDocTemplateHeadTag - JSP - - sets head defaults, such as including the base css sheet and validateForm.js - links. Use this tag if you wish to add more style sheets or javascript to - the head of your HTML doc. - - - title - false - true - - - - - docTemplateBody - blackboard.servlet.tags.ProductionDocTemplateBodyTag - JSP - - Sets the default body tag settings for the HTML page (such as attaching the - default style class to the body tag) - - - onLoad - false - true - - - onUnload - false - true - - - - - coursePage - blackboard.servlet.tags.CoursePageTag - JSP - - Creates a course page element. It may be used to re-arrange elements on a page - from interface to interface. It makes the course available to sub-tags. - - course - Course data object [used in sub-tags to render course-specific elements]; - - - courseId - false - true - - - course - false - true - - - - - courseTitleBar - blackboard.servlet.tags.CourseTitlebarTag - JSP - - Creates a course title bar - - - - - titleBar - blackboard.servlet.tags.TitlebarTag - JSP - - Creates a general use title bar. - iconUrl - the URL for the Icon image used in the titlebar; - pluginId - the Id for the plugin (known by the plugin developer; - and used as the directory root of the plugin, e.g. - "/Blackboard/plugins/*pluginId*/images/pluginimg.gif") - - - iconUrl - false - true - - - pluginId - false - true - - - - - step - blackboard.servlet.tags.StepTag - JSP - - Creates html for a wizard Step element. - number - the number for the step (1, 2, 3 for 1st, 2nd, 3rd in wizard); - title - the title of the step; - - - number - false - true - - - title - true - true - - - width - false - true - - - - - stepSubmit - blackboard.servlet.tags.StepSubmitTag - empty - - Creates html for ane entire submit/cancel step element. - number - the number for the step (1, 2, 3 for 1st, 2nd, 3rd in wizard); - title - the title of the step; - cancelUrl - the url to recall the page to if the user cancels; - instructions - the message string displayed in this step; - - - number - false - true - - - title - false - true - - - cancelUrl - false - true - - - instructions - false - true - - - - - - breadcrumbBar - blackboard.servlet.tags.BreadcrumbBarTag - JSP - - Creates the framework for the breadcrumb navigation. - environment - whether we are viewing this navigation in the - course, control panel, portal, etc. - Acceptable values are: - PORTAL, - COURSE, - CTRL_PANEL, - SYS_ADMIN; - handle - the database field used to uniquely identify the - root nav item for the breadcrumb bar. (bar gets the appropriate - nav item from the handler, then traverses the navigation tree to - iteratively get the item's parents; - target - the default target for bc items in this bar, defaults to none - - - environment - false - true - - - handle - false - true - - - - isContent - false - true - - - target - false - true - - - - - - - breadcrumb - blackboard.servlet.tags.BreadcrumbTag - JSP - - Navigation item for the breadcrumb bar. - href - the optional attribute setting the link for the element; - target - the target attribute for the links in this breadcrumb, defaults - to none - - - href - false - true - - - target - false - true - - - - - - dataElement - blackboard.servlet.tags.DataElementTag - JSP - - This tag will be superceded by type-safe form input tags. - label - the text label for the data (form) element; - should contain: form input tags. - required - indicates that the element is required before submitting - form. (Doesn't validate, simply adds *); - - - label - false - true - - - required - false - true - - - - - instructions - blackboard.servlet.tags.InstructionsTag - JSP - - Instructions should only be called inside a step. - - - - - - stepContent - blackboard.servlet.tags.StepContentTag - JSP - - StepContent should only be called inside a step. It wraps the enclosed - HTML in the appropriate table row and table data code to fit inside the - step tag. It is comparable to the specialized tags Instructions and - DataElement, but is more generalized since it ONLY contains table code. - - - - - - button - blackboard.servlet.tags.ButtonTag - empty - - This tag will be superceded by type-safe button tags. - It hides the details of a button object (eventually will be functional - in javascript and non-javascript modes). - - type - whether the buton is FORM_ACTION, INLINE, LONG_INLINE, or TOOLBAR; - - FORM_ACTION corresponds to the "submit"|"cancel" button type - ACTION_INLINE corresponds to the "modify"|"remove" button type - LONG_INLINE corresponds to inline buttons that don't have the standard button size (e.g., "properties") - TOOLBAR corresponds to the toolbar buttons (e.g. "add item") BUT NOT buttons used in actionbars (only old style toolbar buttons) - - - name - the name of the button, which should correspond to the name of the - image for the button, minus the "_off.gif"; - alt - used for the alt tag for the code; - action - whether the button submits a form or links to another page; - targetUrl - the target for the page to link to, if it's a link-type button; - tabindex - corresponds to the html tabindex property, used to set the tab order - of a given link or button - - - type - true - true - - - name - true - true - - - alt - true - true - - - action - true - true - - - targetUrl - false - true - - - tabindex - false - true - - - - - - receipt - blackboard.servlet.tags.ReceiptTag - JSP - - Creates a receipt page using titleBar and button components. - iconUrl - the URL for the Icon image used in the titlebar; - pluginId - the Id for the plugin (known by the plugin developer - and used as the directory root of the plugin, e.g. - "/Blackboard/plugins/*pluginId*/images/pluginimg.gif"); - recallUrl - OK button, if null will attempt to locate recallUrl from nav item - or use javascript back ONLY SHOWN IF submitUrl and cancelUrl are both null - submitUrl, cancelUrl - optional attributes to allow the receipt to have - two buttons (although either may be used alone).LINK rather than FORM_SUBMIT action - - - - type - false - true - - - iconUrl - false - true - - - pluginId - false - true - - - title - false - true - - - recallUrl - false - true - - - cancelUrl - false - true - - - - buttonName - false - true - - - - buttonAlt - false - true - - - - - - list - blackboard.servlet.tags.ListTag - blackboard.servlet.tags.ListTei - JSP - - Creates a list (UI determined by the TYPE attribute). - collection - the collection passed to the list - (usually a Blackboard filteredList resulting from a Load attempt - within the persistence layer. example: a list of users in a course) - collectionLabel - a label for the objects inthe list, default is "Objects" - object - the BbObject that will be iterated over in the list (example: user) - type - the type of list (determines the UI) - description - description of the list (required for accessibility compliance) - sortUrl - the URL to sort or paginate this list, probably of the form - "myPage.jsp?id=_1_2&name=test" or to control the list display, also specify - "myPage.jsp?id=_1_2&name=test&sortBy=" + listElement_index + - "&startIndex=" + ind - resultsPerPage - the number of items to display on a given page. Default is 20, and - -1 is "show all" - - - collection - true - true - - - objectId - true - true - - - className - true - true - - - type - false - true - - - description - false - true - - - sortUrl - false - true - - - resultsPerPage - false - true - - - collectionLabel - false - true - - - - - listElement - blackboard.servlet.tags.ListElementTag - JSP - - Creates a list item (one "cell" in a list)-- these items will be built up - into rows and rendered as a list (examples: First Name, Last Name, Email) - width - the optional width setting (used for the width of the column of the list) - label - the optional label for the column - href - the optional url to use to sort the list by - abbr - the abbreviation for screen readers (required for accessibility if a LABEL is supplied) - comparator - an optional attribute to specify a comparator used to sort the list. - - - width - false - true - - - label - false - true - - - href - false - true - - - abbr - false - true - - - comparator - false - true - - - name - false - true - - - nowrap - false - true - - - align - false - true - - - valign - false - true - - - - - - simpleList - blackboard.servlet.tags.SimpleListTag - empty - - Creates a list (UI determined by the TYPE attribute). - collection - the collection passed to the list - (usually a Blackboard filteredList resulting from a Load attempt - within the persistence layer. example: a list of users in a course) - type - the type of list (determines the UI) - description - description of the list (required for accessibility compliance) - - - collection - true - true - - - type - false - true - - - description - true - true - - - - - - dateAvailability - - - blackboard.servlet.tags.DateAvailabilityTag - - empty - - creates the HTML and JavaScript for the date dropdown and - validation - - - TO CONNECT WITH FORM HANDLING: - - Read from hidden elements: - data__course_contents___pk1_pk2__restrict_start_date - data__course_contents___pk1_pk2__restrict_end_date - On edit, write to tag Date startDate and Date endDate - - NEW: can now rename these hidden fields with - startDateField - and - endDateField - - For use with the blackboard.struts.DateAvailabilityForm, set - startDateField = "blackboard.struts.DateAvailabilityForm.startDateField"; - endDateField = "blackboard.struts.DateAvailabilityForm.endDateField"; - - pickdate //unknown - pickname //unknown - - make sure that you include a link to the javascript file "/javascript/validateForm.js" - in the jsp page when this widget is used, if you are not using the docTemplate or docTemplateHead - widgets. (this link has been removed - from the widget due to possible javascript conflicts) - - - - - formName - - true - - true - - - - - startCaption - - false - - true - - - - endCaption - - false - - true - - - - startDateField - - false - - true - - - - endDateField - - false - - true - - - - startDate - - false - - true - - - - endDate - - false - - true - - - - - - dateSelection - blackboard.servlet.tags.DateSelectionTag - empty - - Creates the HTML for a date dropdown and related JavaScript validation - - TO CONNECT WITH FORM HANDLING: - Read from hidden elements: - data__course_contents___pk1_pk2__restrict_start_date - On edit, write to tag Date startDate and Date endDate - - - - formName - true - true - - - - elementName - false - true - - - - date - false - true - - - - - caretList - blackboard.servlet.tags.CaretListTag - JSP - - Widget encapsulating a Caret List. - It works like the BreadcrumbBarTag, in that it can accept either - child tags (caret) or a collection of CaretBeans (blackboard.servlet.data.CaretBean). - - description: the table summary (for accessibility/screenreader functionality), describes - the function of the caret list. - - - - description - false - true - - - collection - false - true - - - - - caret - blackboard.servlet.tags.CaretTag - JSP - - Entry in a Caret List. Contains a title, description, and href - (which is a link for the title attribute). Only the title is - required. - The description is set in the body of the tag - - - title - true - true - - - href - false - true - - - - - module - blackboard.servlet.tags.ModuleTag - JSP - - Creates html for a module. - - - title - true - true - - - bgColor - false - true - - - borderColor - false - true - - - borderSize - false - true - - - titleBgColor - false - true - - - titleColor - false - true - - - layoutUIStyle - false - true - - - - - - actionItem - blackboard.servlet.tags.ActionItemTag - Empty - - Action items (such as Add Content), which appear in the - ActionBar - - - title - true - true - - - href - true - true - - - imgUrl - true - true - - - - - actionBar - blackboard.servlet.tags.ActionBarTag - JSP - - Action items (such as Add Content) appear in the - ActionBar - - - action - false - true - - - icon - false - true - - - maxItems - false - true - - - collection - false - true - - - - - colorPicker - blackboard.servlet.tags.ColorPickerTag - empty - - Creates an form element for choosing a color. - - name - Name of the form element; - color - Current color; - - - name - true - true - - - color - false - true - - - formName - false - true - - - - - modulePersonalizationPage - blackboard.servlet.tags.ModulePersonalizationPageTag - JSP - - Creates the header and footer for a module personalization page. - - - title - false - true - - - iconUrl - false - true - - - - - modulePersonalizationReceipt - blackboard.servlet.tags.ModulePersonalizationReceiptTag - JSP - - Creates a module personalization receipt. - - - title - false - true - - - iconUrl - false - true - - - recallUrl - false - true - - - - - moduleAdminPage - blackboard.servlet.tags.ModuleAdminPageTag - JSP - - Creates the header and footer for a module personalization page. - - - title - false - true - - - iconUrl - false - true - - - - - moduleAdminReceipt - blackboard.servlet.tags.ModuleAdminReceiptTag - JSP - - Creates a module personalization receipt. - - - title - false - true - - - iconUrl - false - true - - - recallUrl - false - true - - - - - - - textbox - blackboard.servlet.tags.TextboxTag - empty - - Creates a textbox tag with formatting and MathML controls. - - isMathML - should the widget include the MathML links? - isFormattedText - should the widget include radio buttons for smart/plain/html? - mode - modify|insert - name - the name of the text area control, used in JS as well - label - label for textarea - rows - number of rows for textarea - cols - number of columns for textarea - minLength - validation-- must have a min length of this - maxLength - text content can be no longer than maxLength (validation - text - the text value of the area - xContent - the structured text, in xml - format - smarttext|plaintext|html - includeJs - should only be true ONCE on a page-- set for the first MathML element on page - popupType - defaults to html-- but can configure in future to popup.jsp instead - - - - - isMathML - false - true - - - - - isFormattedText - false - true - - - - mode - false - true - - - - name - true - true - - - - - label - false - true - - - - rows - false - true - - - - - cols - false - true - - - - - minLength - false - true - - - - maxLength - false - true - - - - - text - false - true - - - - - xContent - false - true - - - - - format - false - true - - - - - includeJs - false - true - - - - - popupType - false - true - - - - - - - toolbar - blackboard.servlet.tags.ToolbarTag - JSP - A generic toolbar - - borderColor - false - true - - - toolbarColor - false - true - - - height - false - true - - - width - false - true - - - styleClass - false - true - - - - - toolbarButton - blackboard.servlet.tags.ToolbarButtonTag - JSP - Very similar to an ActionItem containing a link, label and icon - - label - false - true - - - link - true - true - - - icon - false - true - - - iconAlternative - false - true - - - iconHeight - false - true - - - iconWidth - false - true - - - - - toolbarDivider - blackboard.servlet.tags.ToolbarDividerTag - JSP - A vertical divider element - - height - false - true - - - - - toolbarEmpty - blackboard.servlet.tags.ToolbarEmptyTag - JSP - A completely empty element ... do with it what you will, but check the documentation for toolbar eccentricities - - - - toolbarForm - blackboard.servlet.tags.ToolbarFormTag - JSP - Wrapper for toolbar form elements - - name - false - true - - - action - true - true - - - method - false - true - - - encType - false - true - - - - - toolbarText - blackboard.servlet.tags.ToolbarTextTag - JSP - A text input form element - - inputId - true - true - - - label - false - true - - - size - false - true - - - value - false - true - - - icon - false - true - - - iconAlternative - false - true - - - iconHeight - false - true - - - iconWidth - false - true - - - - - toolbarSelect - blackboard.servlet.tags.ToolbarSelectTag - JSP - Info. - - inputId - true - true - - - label - false - true - - - icon - false - true - - - iconAlternative - false - true - - - iconHeight - false - true - - - iconWidth - false - true - - - - - - - navigationItemMenu - blackboard.servlet.tags.NavigationItemMenuTag - JSP - - A box defined by a cascading style sheet, containing all the navigation items belonging to a - certain navigation family. Similar to a caret list, but designed for inline navigation links. - - - family - true - true - - - id - false - true - - - substitutionMap - false - true - - - - - - box - blackboard.servlet.tags.BoxTag - JSP - - A basic box. The body of the tag is included inside the box. Border color - and background color can be specified for visual interfaces. - - - borderColor - false - true - - - backgroundColor - false - true - - - - - search - blackboard.servlet.tags.SearchTag - JSP - - A tabbed search interface. Wraps the UI html around whatever content is in the body of - the tag - - Attributes: - isShowAdvanced - defaults to false. if true, displays a second tab, for Advanced search, - and adds hyperlink wrappers for the inactive tab (note: tabbing between the two - views, normal and advanced, is handled by the tab. If a searchUrl (below) is given, - the hyperlink for the tabs is created by appending "&mode=advanced|normal". Otherwise, - a URL is determined from the request. - searchUrl: an optional attribute used to construct the hyperlink for the Advanced|Normal - tabs. Default- constructed from the request. - - - isShowAdvanced - false - true - - - searchUrl - false - true - - - - - - error - blackboard.servlet.tags.ErrorTag - empty - - Handles exception UI - - exception - the exception that was thrown - - - exception - true - true - - - - - - - - - - - - - - dateRangePicker - - - blackboard.servlet.tags.DatePickerTag - - empty - - creates the HTML and JavaScript for the date dropdown and - validation - - Calendar startDate - if null, defaults to the current time - Calendar endDate - if null, end date dropdown is omitted - - int datePickerIndex - defaults to 0, should be incremented for additional pickers on a page - (NOTE: datePicker and dateRangePicker use the same field naming convention, and should not have the same index if on a page together) - - String formName - defaults to "forms[0]", should be replaced by the form name attribute if more than one form is used on the page - String startCaption - label to use for start date picker, defaults to "Start" - String endCaption - label to use for the end date picker, defaults to "End" - String endDateField - see below - String startDateField - see below - - - boolean checkPastDue - defaults to false, set to true if js validation for past due events is needed - boolean isEndChecked - defaults to false, set to true if the end date is set in the data object (as opposed to an as-yet-unset end date) - - - TO CONNECT WITH FORM HANDLING: - Hidden field names are set with - startDateField - and - endDateField - By default, read from hidden inputs: - start_date_0 - end_date_0 - (if manually setting these names, form name is rendered to page as: - startDateField+"_" + datePickerIndex, to allow multiple pickers per page) - - make sure that you include a link to the javascript file "/javascript/validateForm.js" - in the jsp page when this widget is used, if you are not using the docTemplate or docTemplateHead - widgets. (this link has been removed - from the widget due to possible javascript conflicts) - - - - - formName - - false - - true - - - - - startCaption - - false - - true - - - - endCaption - - false - - true - - - - startDateField - - false - - true - - - - endDateField - - false - - true - - - - startDate - - false - - true - - - - endDate - - false - - true - - - - datePickerIndex - - false - - true - - - - - checkPastDue - - false - - true - - - - - isEndChecked - - false - - true - - - - - - - - - - - - - - datePicker - - - blackboard.servlet.tags.DatePickerTag - - empty - - creates the HTML and JavaScript for the date dropdown and - validation for a single date picker element - - Calendar startDate - if null, defaults to the current time - - int datePickerIndex - defaults to 0, should be incremented for additional pickers on a page - (NOTE: datePicker and dateRangePicker use the same field naming convention, and should not have the same index if on a page together) - - String formName - defaults to "forms[0]", should be replaced by the form name attribute if more than one form is used on the page - String startCaption - label to use for start date picker, defaults to "Start" - String startDateField - see below - - - boolean checkPastDue - defaults to false, set to true if js validation for past due events is needed - - - TO CONNECT WITH FORM HANDLING: - Hidden field name is set with - startDateField - By default, read from hidden inputs: - start_date_0 - (if manually setting these names, form name is rendered to page as: - startDateField+"_" + datePickerIndex, to allow multiple pickers per page) - - make sure that you include a link to the javascript file "/javascript/validateForm.js" - in the jsp page when this widget is used, if you are not using the docTemplate or docTemplateHead - widgets. (this link has been removed - from the widget due to possible javascript conflicts) - - - - - formName - - false - - true - - - - - startCaption - - false - - true - - - - startDateField - - false - - true - - - - startDate - - false - - true - - - - datePickerIndex - - false - - true - - - - - checkPastDue - - false - - true - - - - - - - errors - blackboard.servlet.tags.ErrorsTag - empty - - This is a replacement for the Struts html:error tag, which does not - include the internationalization features. It is assumed that the object - specified by "name" is an ActionErrors object, and that for each - ActionError it contains, the "key" property is actually the literal - message that should be displayed, and not a key into some ResourceBundle. - - Note that, due to a dependency on Struts, and the fact that Struts 1.0 - cannot be loaded from the systemlib classloader, this class is NOT built - into the bb-taglib-api.jar. Instead, if you need to use this tag, you - must share the class into your webapp src directory. See tmoore for info. - - - name - false - true - - - property - false - true - - - - - ppgBase - blackboard.servlet.tags.PpgBaseTag - empty - - - - + + + + + + + + + 1.0 + 1.1 + Blackboard UI Tag Library + /bbUI + + The Blackboard User Interface tag library encapsulates Blackboard user + interface elements to allow developers easy access to the Blackboard look and feel. + + + + loginWelcome + blackboard.servlet.tags.LoginWelcomeTag + empty + + Creates the standard login welcome panel. + + + errorMessage + false + true + + + + + + loginForm + blackboard.servlet.tags.LoginFormTag + empty + + Creates the standard login form ( same look and feel as rendered by login.pl ). + + validateFormScript - name of the inline script which will validate the login form. + errorMessage - error message from previous login attempt + + + validateFormScript + false + true + + + errorMessage + false + true + + + target + false + true + + + + + createAccountForm + blackboard.servlet.tags.CreateAccountFormTag + empty + + Creates the create-account form. + + createAccountLink - name of the page that will create a new user account. + + + createAccountLink + false + true + + + + + copyright + blackboard.servlet.tags.CopyrightTag + empty + + Standard HTML snippet for Blackboard copyright notice. + + + + + validateLoginFormScript + blackboard.servlet.tags.ValidateLoginFormScriptTag + empty + + Validates login form. + + + + + inlineScript + blackboard.servlet.tags.InlineScriptTag + JSP + + Inserts inline script into HTML. + + scriptLanguage - assumed to be JavaScript, if no input is provided. + scriptRef1 - reference to existing script. + scriptRef2 - reference to another existing script. + scriptRef3 - reference to another existing script. + scriptRef4 - reference to another existing script. + + + scriptLanguage + false + true + + + scriptRef1 + false + true + + + scriptRef2 + false + true + + + scriptRef3 + false + true + + + scriptRef4 + false + true + + + + + spacer + blackboard.servlet.tags.SpacerTag + empty + + Creates an invisible area on the page with the specified height + and width. + + height - Vertical size of the spacer in pixels [1]; + width - Horizontal size of the spacer in pixels [1]; + + + height + false + true + + + width + false + true + + + align + false + true + + + + + devDocTemplate + blackboard.servlet.tags.DocumentTemplateTag + JSP + + combines the docTemplateHead and docTemplateBody tag (for use + when you don't need to add items to the HEAD of the HTML doc. + + + title + true + true + + + + + docTemplate + blackboard.servlet.tags.ProductionDocTemplateTag + JSP + combines the docTemplateHead and docTemplateBody tag (for use + when you don't need to add items to the HEAD of the HTML doc. + + + + title + false + true + + + onLoad + false + true + + + onUnload + false + true + + + onSubmit + false + true + + + + + docTemplateHead + blackboard.servlet.tags.ProductionDocTemplateHeadTag + JSP + + sets head defaults, such as including the base css sheet and validateForm.js + links. Use this tag if you wish to add more style sheets or javascript to + the head of your HTML doc. + + + title + false + true + + + + + docTemplateBody + blackboard.servlet.tags.ProductionDocTemplateBodyTag + JSP + + Sets the default body tag settings for the HTML page (such as attaching the + default style class to the body tag) + + + onLoad + false + true + + + onUnload + false + true + + + + + coursePage + blackboard.servlet.tags.CoursePageTag + JSP + + Creates a course page element. It may be used to re-arrange elements on a page + from interface to interface. It makes the course available to sub-tags. + + course - Course data object [used in sub-tags to render course-specific elements]; + + + courseId + false + true + + + course + false + true + + + + + courseTitleBar + blackboard.servlet.tags.CourseTitlebarTag + JSP + + Creates a course title bar + + + + + titleBar + blackboard.servlet.tags.TitlebarTag + JSP + + Creates a general use title bar. + iconUrl - the URL for the Icon image used in the titlebar; + pluginId - the Id for the plugin (known by the plugin developer; + and used as the directory root of the plugin, e.g. + "/Blackboard/plugins/*pluginId*/images/pluginimg.gif") + + + iconUrl + false + true + + + pluginId + false + true + + + + + step + blackboard.servlet.tags.StepTag + JSP + + Creates html for a wizard Step element. + number - the number for the step (1, 2, 3 for 1st, 2nd, 3rd in wizard); + title - the title of the step; + + + number + false + true + + + title + true + true + + + width + false + true + + + + + stepSubmit + blackboard.servlet.tags.StepSubmitTag + empty + + Creates html for ane entire submit/cancel step element. + number - the number for the step (1, 2, 3 for 1st, 2nd, 3rd in wizard); + title - the title of the step; + cancelUrl - the url to recall the page to if the user cancels; + instructions - the message string displayed in this step; + + + number + false + true + + + title + false + true + + + cancelUrl + false + true + + + instructions + false + true + + + + + + breadcrumbBar + blackboard.servlet.tags.BreadcrumbBarTag + JSP + + Creates the framework for the breadcrumb navigation. + environment - whether we are viewing this navigation in the + course, control panel, portal, etc. + Acceptable values are: + PORTAL, + COURSE, + CTRL_PANEL, + SYS_ADMIN; + handle - the database field used to uniquely identify the + root nav item for the breadcrumb bar. (bar gets the appropriate + nav item from the handler, then traverses the navigation tree to + iteratively get the item's parents; + target - the default target for bc items in this bar, defaults to none + + + environment + false + true + + + handle + false + true + + + + isContent + false + true + + + target + false + true + + + + + + + breadcrumb + blackboard.servlet.tags.BreadcrumbTag + JSP + + Navigation item for the breadcrumb bar. + href - the optional attribute setting the link for the element; + target - the target attribute for the links in this breadcrumb, defaults + to none + + + href + false + true + + + target + false + true + + + + + + dataElement + blackboard.servlet.tags.DataElementTag + JSP + + This tag will be superceded by type-safe form input tags. + label - the text label for the data (form) element; + should contain: form input tags. + required - indicates that the element is required before submitting + form. (Doesn't validate, simply adds *); + + + label + false + true + + + required + false + true + + + + + instructions + blackboard.servlet.tags.InstructionsTag + JSP + + Instructions should only be called inside a step. + + + + + + stepContent + blackboard.servlet.tags.StepContentTag + JSP + + StepContent should only be called inside a step. It wraps the enclosed + HTML in the appropriate table row and table data code to fit inside the + step tag. It is comparable to the specialized tags Instructions and + DataElement, but is more generalized since it ONLY contains table code. + + + + + + button + blackboard.servlet.tags.ButtonTag + empty + + This tag will be superceded by type-safe button tags. + It hides the details of a button object (eventually will be functional + in javascript and non-javascript modes). + + type - whether the buton is FORM_ACTION, INLINE, LONG_INLINE, or TOOLBAR; + + FORM_ACTION corresponds to the "submit"|"cancel" button type + ACTION_INLINE corresponds to the "modify"|"remove" button type + LONG_INLINE corresponds to inline buttons that don't have the standard button size (e.g., "properties") + TOOLBAR corresponds to the toolbar buttons (e.g. "add item") BUT NOT buttons used in actionbars (only old style toolbar buttons) + + + name - the name of the button, which should correspond to the name of the + image for the button, minus the "_off.gif"; + alt - used for the alt tag for the code; + action - whether the button submits a form or links to another page; + targetUrl - the target for the page to link to, if it's a link-type button; + tabindex - corresponds to the html tabindex property, used to set the tab order + of a given link or button + + + type + true + true + + + name + true + true + + + alt + true + true + + + action + true + true + + + targetUrl + false + true + + + tabindex + false + true + + + + + + receipt + blackboard.servlet.tags.ReceiptTag + JSP + + Creates a receipt page using titleBar and button components. + iconUrl - the URL for the Icon image used in the titlebar; + pluginId - the Id for the plugin (known by the plugin developer + and used as the directory root of the plugin, e.g. + "/Blackboard/plugins/*pluginId*/images/pluginimg.gif"); + recallUrl - OK button, if null will attempt to locate recallUrl from nav item + or use javascript back ONLY SHOWN IF submitUrl and cancelUrl are both null + submitUrl, cancelUrl - optional attributes to allow the receipt to have + two buttons (although either may be used alone).LINK rather than FORM_SUBMIT action + + + + type + false + true + + + iconUrl + false + true + + + pluginId + false + true + + + title + false + true + + + recallUrl + false + true + + + cancelUrl + false + true + + + + buttonName + false + true + + + + buttonAlt + false + true + + + + + + list + blackboard.servlet.tags.ListTag + blackboard.servlet.tags.ListTei + JSP + + Creates a list (UI determined by the TYPE attribute). + collection - the collection passed to the list + (usually a Blackboard filteredList resulting from a Load attempt + within the persistence layer. example: a list of users in a course) + collectionLabel - a label for the objects inthe list, default is "Objects" + object - the BbObject that will be iterated over in the list (example: user) + type - the type of list (determines the UI) + description - description of the list (required for accessibility compliance) + sortUrl - the URL to sort or paginate this list, probably of the form + "myPage.jsp?id=_1_2&name=test" or to control the list display, also specify + "myPage.jsp?id=_1_2&name=test&sortBy=" + listElement_index + + "&startIndex=" + ind + resultsPerPage - the number of items to display on a given page. Default is 20, and + -1 is "show all" + + + collection + true + true + + + objectId + true + true + + + className + true + true + + + type + false + true + + + description + false + true + + + sortUrl + false + true + + + resultsPerPage + false + true + + + collectionLabel + false + true + + + + + listElement + blackboard.servlet.tags.ListElementTag + JSP + + Creates a list item (one "cell" in a list)-- these items will be built up + into rows and rendered as a list (examples: First Name, Last Name, Email) + width - the optional width setting (used for the width of the column of the list) + label - the optional label for the column + href - the optional url to use to sort the list by + abbr - the abbreviation for screen readers (required for accessibility if a LABEL is supplied) + comparator - an optional attribute to specify a comparator used to sort the list. + + + width + false + true + + + label + false + true + + + href + false + true + + + abbr + false + true + + + comparator + false + true + + + name + false + true + + + nowrap + false + true + + + align + false + true + + + valign + false + true + + + + + + simpleList + blackboard.servlet.tags.SimpleListTag + empty + + Creates a list (UI determined by the TYPE attribute). + collection - the collection passed to the list + (usually a Blackboard filteredList resulting from a Load attempt + within the persistence layer. example: a list of users in a course) + type - the type of list (determines the UI) + description - description of the list (required for accessibility compliance) + + + collection + true + true + + + type + false + true + + + description + true + true + + + + + + dateAvailability + + + blackboard.servlet.tags.DateAvailabilityTag + + empty + + creates the HTML and JavaScript for the date dropdown and + validation + + + TO CONNECT WITH FORM HANDLING: + + Read from hidden elements: + data__course_contents___pk1_pk2__restrict_start_date + data__course_contents___pk1_pk2__restrict_end_date + On edit, write to tag Date startDate and Date endDate + + NEW: can now rename these hidden fields with + startDateField + and + endDateField + + For use with the blackboard.struts.DateAvailabilityForm, set + startDateField = "blackboard.struts.DateAvailabilityForm.startDateField"; + endDateField = "blackboard.struts.DateAvailabilityForm.endDateField"; + + pickdate //unknown + pickname //unknown + + make sure that you include a link to the javascript file "/javascript/validateForm.js" + in the jsp page when this widget is used, if you are not using the docTemplate or docTemplateHead + widgets. (this link has been removed + from the widget due to possible javascript conflicts) + + + + + formName + + true + + true + + + + + startCaption + + false + + true + + + + endCaption + + false + + true + + + + startDateField + + false + + true + + + + endDateField + + false + + true + + + + startDate + + false + + true + + + + endDate + + false + + true + + + + + + dateSelection + blackboard.servlet.tags.DateSelectionTag + empty + + Creates the HTML for a date dropdown and related JavaScript validation + + TO CONNECT WITH FORM HANDLING: + Read from hidden elements: + data__course_contents___pk1_pk2__restrict_start_date + On edit, write to tag Date startDate and Date endDate + + + + formName + true + true + + + + elementName + false + true + + + + date + false + true + + + + + caretList + blackboard.servlet.tags.CaretListTag + JSP + + Widget encapsulating a Caret List. + It works like the BreadcrumbBarTag, in that it can accept either + child tags (caret) or a collection of CaretBeans (blackboard.servlet.data.CaretBean). + + description: the table summary (for accessibility/screenreader functionality), describes + the function of the caret list. + + + + description + false + true + + + collection + false + true + + + + + caret + blackboard.servlet.tags.CaretTag + JSP + + Entry in a Caret List. Contains a title, description, and href + (which is a link for the title attribute). Only the title is + required. + The description is set in the body of the tag + + + title + true + true + + + href + false + true + + + + + module + blackboard.servlet.tags.ModuleTag + JSP + + Creates html for a module. + + + title + true + true + + + bgColor + false + true + + + borderColor + false + true + + + borderSize + false + true + + + titleBgColor + false + true + + + titleColor + false + true + + + layoutUIStyle + false + true + + + + + + actionItem + blackboard.servlet.tags.ActionItemTag + Empty + + Action items (such as Add Content), which appear in the + ActionBar + + + title + true + true + + + href + true + true + + + imgUrl + true + true + + + + + actionBar + blackboard.servlet.tags.ActionBarTag + JSP + + Action items (such as Add Content) appear in the + ActionBar + + + action + false + true + + + icon + false + true + + + maxItems + false + true + + + collection + false + true + + + + + colorPicker + blackboard.servlet.tags.ColorPickerTag + empty + + Creates an form element for choosing a color. + + name - Name of the form element; + color - Current color; + + + name + true + true + + + color + false + true + + + formName + false + true + + + + + modulePersonalizationPage + blackboard.servlet.tags.ModulePersonalizationPageTag + JSP + + Creates the header and footer for a module personalization page. + + + title + false + true + + + iconUrl + false + true + + + + + modulePersonalizationReceipt + blackboard.servlet.tags.ModulePersonalizationReceiptTag + JSP + + Creates a module personalization receipt. + + + title + false + true + + + iconUrl + false + true + + + recallUrl + false + true + + + + + moduleAdminPage + blackboard.servlet.tags.ModuleAdminPageTag + JSP + + Creates the header and footer for a module personalization page. + + + title + false + true + + + iconUrl + false + true + + + + + moduleAdminReceipt + blackboard.servlet.tags.ModuleAdminReceiptTag + JSP + + Creates a module personalization receipt. + + + title + false + true + + + iconUrl + false + true + + + recallUrl + false + true + + + + + + + textbox + blackboard.servlet.tags.TextboxTag + empty + + Creates a textbox tag with formatting and MathML controls. + + isMathML - should the widget include the MathML links? + isFormattedText - should the widget include radio buttons for smart/plain/html? + mode - modify|insert + name - the name of the text area control, used in JS as well + label - label for textarea + rows - number of rows for textarea + cols - number of columns for textarea + minLength - validation-- must have a min length of this + maxLength - text content can be no longer than maxLength (validation + text - the text value of the area + xContent - the structured text, in xml + format - smarttext|plaintext|html + includeJs - should only be true ONCE on a page-- set for the first MathML element on page + popupType - defaults to html-- but can configure in future to popup.jsp instead + + + + + isMathML + false + true + + + + + isFormattedText + false + true + + + + mode + false + true + + + + name + true + true + + + + + label + false + true + + + + rows + false + true + + + + + cols + false + true + + + + + minLength + false + true + + + + maxLength + false + true + + + + + text + false + true + + + + + xContent + false + true + + + + + format + false + true + + + + + includeJs + false + true + + + + + popupType + false + true + + + + + + + toolbar + blackboard.servlet.tags.ToolbarTag + JSP + A generic toolbar + + borderColor + false + true + + + toolbarColor + false + true + + + height + false + true + + + width + false + true + + + styleClass + false + true + + + + + toolbarButton + blackboard.servlet.tags.ToolbarButtonTag + JSP + Very similar to an ActionItem containing a link, label and icon + + label + false + true + + + link + true + true + + + icon + false + true + + + iconAlternative + false + true + + + iconHeight + false + true + + + iconWidth + false + true + + + + + toolbarDivider + blackboard.servlet.tags.ToolbarDividerTag + JSP + A vertical divider element + + height + false + true + + + + + toolbarEmpty + blackboard.servlet.tags.ToolbarEmptyTag + JSP + A completely empty element ... do with it what you will, but check the documentation for toolbar eccentricities + + + + toolbarForm + blackboard.servlet.tags.ToolbarFormTag + JSP + Wrapper for toolbar form elements + + name + false + true + + + action + true + true + + + method + false + true + + + encType + false + true + + + + + toolbarText + blackboard.servlet.tags.ToolbarTextTag + JSP + A text input form element + + inputId + true + true + + + label + false + true + + + size + false + true + + + value + false + true + + + icon + false + true + + + iconAlternative + false + true + + + iconHeight + false + true + + + iconWidth + false + true + + + + + toolbarSelect + blackboard.servlet.tags.ToolbarSelectTag + JSP + Info. + + inputId + true + true + + + label + false + true + + + icon + false + true + + + iconAlternative + false + true + + + iconHeight + false + true + + + iconWidth + false + true + + + + + + + navigationItemMenu + blackboard.servlet.tags.NavigationItemMenuTag + JSP + + A box defined by a cascading style sheet, containing all the navigation items belonging to a + certain navigation family. Similar to a caret list, but designed for inline navigation links. + + + family + true + true + + + id + false + true + + + substitutionMap + false + true + + + + + + box + blackboard.servlet.tags.BoxTag + JSP + + A basic box. The body of the tag is included inside the box. Border color + and background color can be specified for visual interfaces. + + + borderColor + false + true + + + backgroundColor + false + true + + + + + search + blackboard.servlet.tags.SearchTag + JSP + + A tabbed search interface. Wraps the UI html around whatever content is in the body of + the tag + + Attributes: + isShowAdvanced - defaults to false. if true, displays a second tab, for Advanced search, + and adds hyperlink wrappers for the inactive tab (note: tabbing between the two + views, normal and advanced, is handled by the tab. If a searchUrl (below) is given, + the hyperlink for the tabs is created by appending "&mode=advanced|normal". Otherwise, + a URL is determined from the request. + searchUrl: an optional attribute used to construct the hyperlink for the Advanced|Normal + tabs. Default- constructed from the request. + + + isShowAdvanced + false + true + + + searchUrl + false + true + + + + + + error + blackboard.servlet.tags.ErrorTag + empty + + Handles exception UI + + exception - the exception that was thrown + + + exception + true + true + + + + + + + + + + + + + + dateRangePicker + + + blackboard.servlet.tags.DatePickerTag + + empty + + creates the HTML and JavaScript for the date dropdown and + validation + + Calendar startDate - if null, defaults to the current time + Calendar endDate - if null, end date dropdown is omitted + + int datePickerIndex - defaults to 0, should be incremented for additional pickers on a page + (NOTE: datePicker and dateRangePicker use the same field naming convention, and should not have the same index if on a page together) + + String formName - defaults to "forms[0]", should be replaced by the form name attribute if more than one form is used on the page + String startCaption - label to use for start date picker, defaults to "Start" + String endCaption - label to use for the end date picker, defaults to "End" + String endDateField - see below + String startDateField - see below + + + boolean checkPastDue - defaults to false, set to true if js validation for past due events is needed + boolean isEndChecked - defaults to false, set to true if the end date is set in the data object (as opposed to an as-yet-unset end date) + + + TO CONNECT WITH FORM HANDLING: + Hidden field names are set with + startDateField + and + endDateField + By default, read from hidden inputs: + start_date_0 + end_date_0 + (if manually setting these names, form name is rendered to page as: + startDateField+"_" + datePickerIndex, to allow multiple pickers per page) + + make sure that you include a link to the javascript file "/javascript/validateForm.js" + in the jsp page when this widget is used, if you are not using the docTemplate or docTemplateHead + widgets. (this link has been removed + from the widget due to possible javascript conflicts) + + + + + formName + + false + + true + + + + + startCaption + + false + + true + + + + endCaption + + false + + true + + + + startDateField + + false + + true + + + + endDateField + + false + + true + + + + startDate + + false + + true + + + + endDate + + false + + true + + + + datePickerIndex + + false + + true + + + + + checkPastDue + + false + + true + + + + + isEndChecked + + false + + true + + + + + + + + + + + + + + datePicker + + + blackboard.servlet.tags.DatePickerTag + + empty + + creates the HTML and JavaScript for the date dropdown and + validation for a single date picker element + + Calendar startDate - if null, defaults to the current time + + int datePickerIndex - defaults to 0, should be incremented for additional pickers on a page + (NOTE: datePicker and dateRangePicker use the same field naming convention, and should not have the same index if on a page together) + + String formName - defaults to "forms[0]", should be replaced by the form name attribute if more than one form is used on the page + String startCaption - label to use for start date picker, defaults to "Start" + String startDateField - see below + + + boolean checkPastDue - defaults to false, set to true if js validation for past due events is needed + + + TO CONNECT WITH FORM HANDLING: + Hidden field name is set with + startDateField + By default, read from hidden inputs: + start_date_0 + (if manually setting these names, form name is rendered to page as: + startDateField+"_" + datePickerIndex, to allow multiple pickers per page) + + make sure that you include a link to the javascript file "/javascript/validateForm.js" + in the jsp page when this widget is used, if you are not using the docTemplate or docTemplateHead + widgets. (this link has been removed + from the widget due to possible javascript conflicts) + + + + + formName + + false + + true + + + + + startCaption + + false + + true + + + + startDateField + + false + + true + + + + startDate + + false + + true + + + + datePickerIndex + + false + + true + + + + + checkPastDue + + false + + true + + + + + + + errors + blackboard.servlet.tags.ErrorsTag + empty + + This is a replacement for the Struts html:error tag, which does not + include the internationalization features. It is assumed that the object + specified by "name" is an ActionErrors object, and that for each + ActionError it contains, the "key" property is actually the literal + message that should be displayed, and not a key into some ResourceBundle. + + Note that, due to a dependency on Struts, and the fact that Struts 1.0 + cannot be loaded from the systemlib classloader, this class is NOT built + into the bb-taglib-api.jar. Instead, if you need to use this tag, you + must share the class into your webapp src directory. See tmoore for info. + + + name + false + true + + + property + false + true + + + + + ppgBase + blackboard.servlet.tags.PpgBaseTag + empty + + + + diff --git a/oeqAuditB2/src/main/webapp/taglibs/bbUI.xsl b/oeqAuditB2/src/main/webapp/taglibs/bbUI.xsl index 33a0082..a7a8f01 100644 --- a/oeqAuditB2/src/main/webapp/taglibs/bbUI.xsl +++ b/oeqAuditB2/src/main/webapp/taglibs/bbUI.xsl @@ -1,32 +1,32 @@ - - - - - -
    - -

  1. - -
      - - -
    • Body:
    • - -
    • Info:


    • - - Attributes: - -
        -

      • - Required:
        - Real-time evaluated:
        -
      -
      -


    - -
    -
- - -
-
+ + + + + +
    + +

  1. + +
      + + +
    • Body:
    • + +
    • Info:


    • + + Attributes: + +
        +

      • + Required:
        + Real-time evaluated:
        +
      +
      +


    + +
    +
+ + +
+
diff --git a/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/BbLogger.java b/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/BbLogger.java index 4cc25e9..1518c7e 100644 --- a/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/BbLogger.java +++ b/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/BbLogger.java @@ -1,5 +1,8 @@ package org.apereo.openequella.integration.blackboard.common; +import java.util.Map; +import java.util.Map.Entry; + import blackboard.platform.log.LogService; import blackboard.platform.log.LogServiceFactory; @@ -7,6 +10,14 @@ // Logging Levels public class BbLogger { + // public static void main(String[] args) { + // Map m = new HashMap<>(); + // m.put("key1", "value1"); + // for (Entry e : m.entrySet()) { + // System.out.println("K=[" + e.getKey() + "], V=[" + e.getValue() + "]"); + // } + // } + private static final String SIG = "oeqInteg - "; private static final LogService LOGGER = LogServiceFactory.getInstance(); @@ -43,14 +54,21 @@ public synchronized void setLoggingLevel(LogLevel level) { logLevel = level; } - public void logTrace(String msg) { - if (logLevel == LogLevel.NotSet) { - LOGGER.logAudit(SIG + msg); - } else if (logLevel == LogLevel.Trace) { - LOGGER.logWarning(SIG + "Trace - " + msg); + public void logTrace(String msg, Map m) { + logTrace(msg); + for (Entry e : m.entrySet()) { + logTrace("K=[" + e.getKey() + "], V=[" + e.getValue() + "]"); } } + public void logTrace(String msg) { + // if (logLevel == LogLevel.NotSet) { + // LOGGER.logAudit(SIG + msg); + // } else if (logLevel == LogLevel.Trace) { + LOGGER.logWarning(SIG + "Trace - " + msg); + // } + } + public void logSqlTrace(String msg) { if (logLevel == LogLevel.NotSet) { LOGGER.logAudit(SIG + msg); diff --git a/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/BbUtil.java b/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/BbUtil.java index 8c52946..3da9e5d 100644 --- a/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/BbUtil.java +++ b/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/BbUtil.java @@ -33,8 +33,8 @@ @SuppressWarnings("nls") // @NonNullByDefault public class BbUtil { - public static final String HANDLE = "oeqPrimary"; - public static final String VENDOR = "aper"; + public static final String HANDLE = "tle"; + public static final String VENDOR = "dych"; public static final String CONTENT_HANDLER = "resource/tle-resource"; public static final String CONTEXT_TAG = "@X@"; public static final String CONTENT_ID = "content_id"; diff --git a/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/Configuration.java b/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/Configuration.java index d877f5c..4922eee 100644 --- a/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/Configuration.java +++ b/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/Configuration.java @@ -7,7 +7,6 @@ import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; -import java.text.MessageFormat; import java.util.Calendar; import java.util.Collection; import java.util.Date; @@ -18,6 +17,17 @@ import javax.servlet.http.HttpServletRequest; +import org.apereo.openequella.integration.blackboard.common.BbContext; +import org.apereo.openequella.integration.blackboard.common.BbLogger; +import org.apereo.openequella.integration.blackboard.common.BbLogger.LogLevel; +import org.apereo.openequella.integration.blackboard.common.BbUtil; +import org.apereo.openequella.integration.blackboard.common.PathUtils; +import org.apereo.openequella.integration.blackboard.common.content.PlacementUtil; +import org.apereo.openequella.integration.blackboard.common.content.PlacementUtil.LoadPlacementResponse; + +import com.google.common.base.Strings; +import com.google.common.io.Closeables; + import blackboard.data.blti.BasicLTIDomainConfig; import blackboard.data.blti.BasicLTIDomainConfig.SendUserData; import blackboard.data.blti.BasicLTIDomainConfig.Status; @@ -28,621 +38,613 @@ import blackboard.platform.blti.BasicLTIDomainConfigManagerFactory; import blackboard.platform.gradebook2.ScoreProvider; import blackboard.platform.gradebook2.impl.ScoreProviderDAO; -import blackboard.platform.log.LogService; -import blackboard.platform.log.LogServiceFactory; import blackboard.platform.plugin.ContentHandler; import blackboard.platform.plugin.ContentHandlerDbLoader; import blackboard.platform.plugin.ContentHandlerDbPersister; -import blackboard.platform.plugin.PlugInConfig; +import blackboard.platform.plugin.PlugInException; +import blackboard.platform.plugin.PlugInUtil; import blackboard.platform.vxi.data.VirtualHost; import blackboard.platform.vxi.service.VirtualInstallationManager; import blackboard.platform.vxi.service.VirtualInstallationManagerFactory; -import com.google.common.base.Strings; -import com.google.common.base.Throwables; -import com.google.common.io.Closeables; -import org.apereo.openequella.integration.blackboard.common.BbContext; -import org.apereo.openequella.integration.blackboard.common.BbLogger; -import org.apereo.openequella.integration.blackboard.common.BbLogger.LogLevel; -import org.apereo.openequella.integration.blackboard.common.BbUtil; -import org.apereo.openequella.integration.blackboard.common.PathUtils; -import org.apereo.openequella.integration.blackboard.common.content.PlacementUtil; -import org.apereo.openequella.integration.blackboard.common.content.PlacementUtil.LoadPlacementResponse; - @SuppressWarnings("nls") // @NonNullByDefault public class Configuration { - /* @Nullable */ - private static Configuration instance; - private static final Object instanceLock = new Object(); - - private static final String CONFIG_FILE = "config.properties"; - private static final String CONTENT_HANDLER_HANDLE = "resource/tle-resource"; - - // DEPRECATED CONFIG ELEMENTS - private static final String HOST = "host"; - private static final String PORT = "port"; - private static final String CONTEXT = "context"; - private static final String INSTITUTION = "institution"; - private static final String SECURE = "secure"; - private static final String MOCK_PORTAL_ROLES = "mock.portal.roles"; - - // CONFIG ELEMENTS (referred to in the JSP) - public static final String EQUELLA_URL = "equellaurl"; - public static final String SECRET = "secret"; - public static final String SECRETID = "secretid"; - public static final String OAUTH_CLIENT_ID = "oauth.clientid"; - public static final String OAUTH_CLIENT_SECRET = "oauth.clientsecret"; - public static final String RESTRICTIONS = "restrictions"; - public static final String LOG_LEVEL = "loglevel"; - public static final String NEWWINDOW = "newwindow"; - - private static final String DEFAULT_SECRET = ""; - private static final String DEFAULT_RESTRICTION = "none"; - private static final LogLevel DEFAULT_LOG_LEVEL = BbLogger.LogLevel.Info; - - private final BbContext context; - private final PlugInConfig plugin; - /* @Nullable */ - private ContentHandler contentHandler; - private final Object contentHandlerLock = new Object(); - - /* @Nullable */ - private String version; - /* @Nullable */ - private String equellaUrl; - /* @Nullable */ - private String secret; - /* @Nullable */ - private String secretid; - /* @Nullable */ - private String oauthClientId; - /* @Nullable */ - private String oauthClientSecret; - /* @Nullable */ - private String restriction; - /* @Nullable */ - private String logLevel; - private boolean newWindow; - /* @Nullable */ - private Set mockPortalRoles; - - private Date lastModified = Calendar.getInstance().getTime(); - - @SuppressWarnings("null") - public static Configuration instance() { - if (instance == null) { - synchronized (instanceLock) { - if (instance == null) { - instance = new Configuration(); - } - } - } - return instance; - } - - private Configuration() { - try { - plugin = new PlugInConfig(BbUtil.VENDOR, BbUtil.HANDLE); - context = BbContext.instance(); - - final VirtualInstallationManager vim = VirtualInstallationManagerFactory.getInstance(); - final VirtualHost vhost = vim.getVirtualHost(""); - context.getContextManager().setContext(vhost); - - version = loadVersion(); - BbLogger.instance().logTrace("Version: " + version); - - load(); - ensureLtiPlacement(); - ensureScoreProvider(); - // Fix up dodgy Blind SSL - // HttpsURLConnection.setDefaultSSLSocketFactory(new - // sun.security.ssl.SSLSocketFactoryImpl()); - } catch (Exception e) { - BbLogger.instance().logError("Couldn't init building block", e); - throw Throwables.propagate(e); - } - } - - @Override - protected void finalize() throws Throwable { - context.getContextManager().releaseContext(); - super.finalize(); - } - - public synchronized void modify(HttpServletRequest request) throws Exception { - setEquellaUrl(request.getParameter(EQUELLA_URL)); - setSecret(request.getParameter(SECRET)); - setSecretId(request.getParameter(SECRETID)); - setOauthClientId(request.getParameter(OAUTH_CLIENT_ID)); - setOauthClientSecret(request.getParameter(OAUTH_CLIENT_SECRET)); - setRestriction(request.getParameter(RESTRICTIONS)); - setLogLevel(request.getParameter(LOG_LEVEL)); - String newWindowParam = request.getParameter(NEWWINDOW); - if (newWindowParam == null || newWindowParam.equals("")) { - newWindowParam = "false"; - } - setNewWindow(Boolean.parseBoolean(newWindowParam)); - lastModified = Calendar.getInstance().getTime(); - - } - - public synchronized void load() { - final File configFile = new File(getConfigDirectory(), CONFIG_FILE); - if (!configFile.exists()) { - try { - configFile.createNewFile(); - BbLogger.instance().logTrace("Successfully created configuration file"); - } catch (IOException e) { - BbLogger.instance().logError("Error creating configuration file", e); - throw Throwables.propagate(e); - } - return; - } - - FileInputStream fis = null; - try { - fis = new FileInputStream(configFile); - final Properties props = new Properties(); - props.load(fis); - if (props.containsKey(EQUELLA_URL)) { - setEquellaUrl(props.getProperty(EQUELLA_URL)); - } else { - try { - setEquellaUrl(buildEquellaUrlFromDeprecatedConfigParams(props)); - } catch (MalformedURLException mal) { - BbLogger.instance().logTrace("Failed to load equella URL from deprecated props"); - } - } - setSecret(props.getProperty(SECRET)); - setSecretId(props.getProperty(SECRETID)); - setOauthClientId(props.getProperty(OAUTH_CLIENT_ID)); - setOauthClientSecret(props.getProperty(OAUTH_CLIENT_SECRET)); - setMockPortalRoles(commaSplit(props.getProperty(MOCK_PORTAL_ROLES))); - setRestriction(props.getProperty(RESTRICTIONS)); - setLogLevel(props.getProperty(LOG_LEVEL)); - setNewWindow(Boolean.parseBoolean(props.getProperty(NEWWINDOW, "true"))); - } catch (Exception e) { - BbLogger.instance().logError("Error loading configuration", e); - throw Throwables.propagate(e); - } finally { - if (fis != null) { - try { - fis.close(); - } catch (IOException e) { - // Ignore - } - } - } - } - - public synchronized void save() { - final File configFile = new File(getConfigDirectory(), CONFIG_FILE); - FileOutputStream fos = null; - try { - ensureLtiPlacement(); - ensureScoreProvider(); - - fos = new FileOutputStream(configFile); - - Properties props = new Properties(); - props.setProperty(EQUELLA_URL, equellaUrl); - props.setProperty(SECRET, secret); - props.setProperty(SECRETID, secretid); - props.setProperty(OAUTH_CLIENT_ID, oauthClientId); - props.setProperty(OAUTH_CLIENT_SECRET, oauthClientSecret); - props.setProperty(MOCK_PORTAL_ROLES, commaJoin(mockPortalRoles)); - props.setProperty(RESTRICTIONS, restriction); - props.setProperty(LOG_LEVEL, logLevel); - props.setProperty(NEWWINDOW, Boolean.toString(newWindow)); - props.store(fos, null); - } catch (Exception e) { - BbLogger.instance().logError("Error saving configuration", e); - throw Throwables.propagate(e); - } finally { - if (fos != null) { - try { - fos.close(); - } catch (IOException e) { - // Ignore - } - } - } - } - - /** - * @Nullable - * @return - */ - private synchronized BasicLTIPlacement ensureLtiPlacement() { - try { - BbLogger.instance().logTrace("Entering ensureLtiPlacement"); - if (Strings.isNullOrEmpty(oauthClientId) || Strings.isNullOrEmpty(oauthClientSecret) - || Strings.isNullOrEmpty(equellaUrl)) { - BbLogger.instance().logTrace("Not creating a placement since a property was blank"); - return null; - } - - BasicLTIDomainConfig domainConfig = null; - boolean newPlacement = false; - BasicLTIPlacement placement = PlacementUtil.loadFromHandle(CONTENT_HANDLER_HANDLE); - if (placement == null) { - BbLogger.instance().logTrace("Loading placement via URL " + equellaUrl); - final LoadPlacementResponse loadPlacement = PlacementUtil.loadPlacementByUrl(equellaUrl); - placement = loadPlacement.getPlacement(); - domainConfig = loadPlacement.getDomainConfig(); - if (placement == null) { - BbLogger.instance().logTrace("No existing placement for URL " + equellaUrl); - final Id placementId = getContentHandler().getBasicLTIPlacementId(); - if (!Id.isValid(placementId)) { - BbLogger.instance().logTrace("No existing placement associated with ContentHandler"); - - if (domainConfig == null) { - domainConfig = createDomainConfig(); - } - placement = PlacementUtil.createNewPlacement(domainConfig, getContentHandler()); - newPlacement = true; - } else { - BbLogger.instance().logTrace("Loading existing placement from ContentHandler"); - placement = PlacementUtil.loadFromId(placementId); - if (placement == null) { - BbLogger.instance().logTrace("Content handler pointing to invalid placement..."); - if (domainConfig == null) { - domainConfig = createDomainConfig(); - } - placement = PlacementUtil.createNewPlacement(domainConfig, getContentHandler()); - newPlacement = true; - } - } - } else { - BbLogger.instance().logTrace("Loaded placement via URL"); - } - } else { - BbLogger.instance().logTrace("Loaded placement via handle"); - } - - // ensure domainConfig - if (domainConfig == null) { - BbLogger.instance().logTrace("No domain config loaded"); - - // get any current domain config for this domain - domainConfig = PlacementUtil.loadDomainConfigByUrl(new URL(equellaUrl)); - - if (domainConfig != null) { - BbLogger.instance().logTrace("Domain config loaded via URL " + equellaUrl); - if (populateDomainConfig(domainConfig)) { - BbLogger.instance().logTrace("Saving dirty domain config"); - final BasicLTIDomainConfigManager fac = BasicLTIDomainConfigManagerFactory.getInstance(); - fac.save(domainConfig); - } else { - BbLogger.instance().logTrace("Not saving doman config (not dirty)"); - } - } else { - BbLogger.instance().logTrace("No domain config for URL " + equellaUrl); - final Id domainConfigId = placement.getBasicLTIDomainConfigId(); - if (Id.isValid(domainConfigId)) { - BbLogger.instance().logTrace("Loading domain config based on placement pointer"); - final BasicLTIDomainConfigManager fac = BasicLTIDomainConfigManagerFactory.getInstance(); - domainConfig = fac.loadById(domainConfigId); - if (populateDomainConfig(domainConfig)) { - BbLogger.instance().logTrace("Saving dirty domain config"); - fac.save(domainConfig); - } else { - BbLogger.instance().logTrace("Not saving domain config (not dirty)"); - } - } else { - BbLogger.instance().logTrace("Creating new domain config"); - domainConfig = createDomainConfig(); - } - } - } - - BbLogger.instance().logTrace("newPlacement = " + newPlacement); - BbLogger.instance().logTrace("placement = " + placement.getId().toExternalString()); - - boolean saveHandler = newPlacement; - // delete the old placement associated with this handler if - // there is one - final ContentHandler handler = getContentHandler(); - if (handlerPlacementMismatch(placement, handler)) { - if (Id.isValid(handler.getBasicLTIPlacementId())) { - BbLogger.instance().logTrace("Deleting existing placement associated with this handler " - + handler.getBasicLTIPlacementId().toExternalString()); - PlacementUtil.deleteById(handler.getBasicLTIPlacementId()); - } - saveHandler = true; - } - - // Save placement? - // if( newPlacement || requiresSave(placement, domainConfig) ) - // { - placement.setBasicLTIDomainConfigId(domainConfig.getId()); - placement.setUrl(equellaUrl); - PlacementUtil.save(placement); - // } - - // Save handler? - if (saveHandler) { - BbLogger.instance() - .logTrace("Saving updated ContentHandler with placement " + placement.getId().toExternalString()); - - handler.setBasicLTIPlacementId(placement.getId()); - final ContentHandlerDbPersister contentHandlerPersister = (ContentHandlerDbPersister) BbContext.instance() - .getPersistenceManager().getPersister(ContentHandlerDbPersister.TYPE); - contentHandlerPersister.persist(handler); - } else { - BbLogger.instance().logTrace("Not saving ContentHandler"); - } - - return placement; - } catch (Exception e) { - BbLogger.instance().logError("Error ensuring LTI placement", e); - throw Throwables.propagate(e); - } - } - - private boolean handlerPlacementMismatch(BasicLTIPlacement placement, ContentHandler handler) { - final Id currentPlacementId = handler.getBasicLTIPlacementId(); - if (!currentPlacementId.equals(placement.getId())) { - BbLogger.instance().logTrace("ContentHandler is pointing at a different (or null) placement " - + handler.getBasicLTIPlacementId().toExternalString()); - return true; - } - BbLogger.instance().logTrace("ContentHandler is pointing to correct placement"); - return false; - } - - private synchronized ScoreProvider ensureScoreProvider() { - final ScoreProviderDAO dao = ScoreProviderDAO.get(); - ScoreProvider provider = dao.getByHandle(BbUtil.CONTENT_HANDLER); - if (provider == null) { - // The boolean values are cloned from what I could find about - // resource/x-bb-blti-link - // in score_provider.txt in BB installer - provider = new ScoreProvider(); - provider.setName("Equella"); - provider.setHandle(BbUtil.CONTENT_HANDLER); - provider.setAllowAttemptGrading(true); - provider.setAllowMultiple(false); - provider.setAttemptBased(false); - provider.setGradeAction(PathUtils.urlPath(BbUtil.getBlockRelativePath(), "ViewGradebook")); - provider.setReviewAction(PathUtils.urlPath(BbUtil.getBlockRelativePath(), "ViewGradebook")); - dao.persist(provider); - } - - return provider; - } - - private BasicLTIDomainConfig createDomainConfig() { - final BasicLTIDomainConfig domainConfig = new BasicLTIDomainConfig(); - populateDomainConfig(domainConfig); - domainConfig.setSendEmail(true); - domainConfig.setSendName(true); - domainConfig.setSendRole(true); - domainConfig.setUseSplash(false); - domainConfig.setSendUserData(SendUserData.Always); - domainConfig.setStatus(Status.Approved); - final BasicLTIDomainConfigManager fac = BasicLTIDomainConfigManagerFactory.getInstance(); - fac.save(domainConfig); - return domainConfig; - } - - /** - * @param domainConfig - * @return Was changed - */ - private boolean populateDomainConfig(BasicLTIDomainConfig domainConfig) { - try { - boolean dirty = false; - final String key = domainConfig.getKey(); - if (!Strings.nullToEmpty(key).equals(Strings.nullToEmpty(oauthClientId))) { - domainConfig.setKey(oauthClientId); - dirty = true; - } - final String secret = domainConfig.getSecret(); - if (!Strings.nullToEmpty(secret).equals(Strings.nullToEmpty(oauthClientSecret))) { - domainConfig.setSecret(oauthClientSecret); - dirty = true; - } - - BasicLTIDomainHost host = domainConfig.getPrimaryHost(); - final String newDomain = new URL(equellaUrl).getHost(); - if (host == null || !Strings.nullToEmpty(host.getDomain()).equals(Strings.nullToEmpty(newDomain))) { - host = (host == null ? new BasicLTIDomainHost() : host); - host.setDomain(newDomain); - host.setPrimary(true); - domainConfig.setPrimaryHost(host); - dirty = true; - } - return dirty; - } catch (Exception mal) { - BbLogger.instance().logError("Error populating domainConfig", mal); - throw Throwables.propagate(mal); - } - } - - private ContentHandler getContentHandler() throws Exception { - if (contentHandler == null) { - synchronized (contentHandlerLock) { - if (contentHandler == null) { - final ContentHandlerDbLoader contentHandlerLoader = (ContentHandlerDbLoader) BbContext.instance() - .getPersistenceManager().getLoader(ContentHandlerDbLoader.TYPE); - contentHandler = contentHandlerLoader.loadByHandle(CONTENT_HANDLER_HANDLE); - final Id basicLTIPlacementId = contentHandler.getBasicLTIPlacementId(); - BbLogger.instance().logTrace("Loaded content handler from DB, placement = " - + (basicLTIPlacementId == null ? "null" : basicLTIPlacementId.toExternalString())); - } - } - } - return contentHandler; - } - - public static String loadVersion() throws IOException { - InputStream in = null; - try { - // CBEACH: This file wasn't able to be read. maybe a permissions or location - // issue. - // final Properties p = new Properties(); - // in = Configuration.class.getResourceAsStream("/version.properties"); - // p.load(in); - // final String versionB2 = p.getProperty("version.b2"); - // return versionB2; - BbLogger.instance().logTrace("Loading Version."); - return "TODO - need to implement version from file system property."; - } catch (Exception e) { - BbLogger.instance().logError("Couldn't load version", e); - throw Throwables.propagate(e); - } finally { - Closeables.close(in, false); - } - } - - public File getConfigDirectory() { - return plugin.getConfigDirectory(); - } - - private String buildEquellaUrlFromDeprecatedConfigParams(Properties props) throws MalformedURLException { - final boolean secure = Boolean.valueOf(props.getProperty(SECURE)); - final String host = props.getProperty(HOST, "localhost"); - final int port = Integer.parseInt(props.getProperty(PORT, "80")); - final String context = props.getProperty(CONTEXT, "/"); - final String inst = props.getProperty(INSTITUTION, ""); - - return new URL(new URL(secure ? "https" : "http", host, port, context), inst).toString(); - } - - public boolean hasBeenModified(Date lastUpdate) { - return lastUpdate.before(lastModified); - } - - private Set commaSplit(/* @Nullable */String value) { - final Set result = new HashSet(); - if (value != null) { - final String[] vs = value.split(","); - for (int i = 0; i < vs.length; i++) { - result.add(vs[i].trim()); - } - } - return result; - } - - private String commaJoin(/* @Nullable */Collection values) { - final StringBuilder roles = new StringBuilder(); - if (values != null) { - for (Iterator iter = values.iterator(); iter.hasNext();) { - if (roles.length() > 0) { - roles.append(','); - } - roles.append(iter.next()); - } - } - return roles.toString(); - } - - /* @Nullable */ - public String getEquellaUrl() { - return equellaUrl; - } - - public void setEquellaUrl(/* @Nullable */String equellaUrl) { - this.equellaUrl = (equellaUrl == null ? null : equellaUrl.trim()); - if (!Strings.isNullOrEmpty(this.equellaUrl) && !this.equellaUrl.endsWith("/")) { - this.equellaUrl += '/'; - } - } - - /* @Nullable */ - public String getSecret() { - return secret; - } - - public void setSecret(/* @Nullable */String secret) { - if (secret == null || secret.length() == 0) { - this.secret = DEFAULT_SECRET; - } else { - this.secret = secret; - } - } - - /* @Nullable */ - public String getSecretId() { - return secretid; - } - - public void setSecretId(/* @Nullable */String secretid) { - if (secretid == null || secretid.length() == 0) { - this.secretid = DEFAULT_SECRET; - } else { - this.secretid = secretid; - } - } - - /* @Nullable */ - public String getOauthClientId() { - return oauthClientId; - } - - public void setOauthClientId(/* @Nullable */String oauthClientId) { - this.oauthClientId = oauthClientId; - } - - /* @Nullable */ - public String getOauthClientSecret() { - return oauthClientSecret; - } - - public void setOauthClientSecret(/* @Nullable */String oauthClientSecret) { - this.oauthClientSecret = oauthClientSecret; - } - - /* @Nullable */ - public String getVersion() { - return version; - } - - public void setMockPortalRoles(/* @Nullable */Set mockPortalRoles) { - this.mockPortalRoles = mockPortalRoles; - } - - /* @Nullable */ - public Set getMockPortalRoles() { - return mockPortalRoles; - } - - /* @Nullable */ - public String getRestriction() { - return restriction; - } - - public void setRestriction(/* @Nullable */String restriction) { - if (restriction == null) { - this.restriction = DEFAULT_RESTRICTION; - } else { - this.restriction = restriction; - } - } - - /* @Nullable */ - public String getLogLevel() { - return logLevel; - } - - public void setLogLevel(/* @Nullable */String level) { - if (level == null) { - this.logLevel = DEFAULT_LOG_LEVEL.name(); - } else { - this.logLevel = level; - } - BbLogger.instance().setLoggingLevel(LogLevel.valueOf(this.logLevel)); - } - - public void setNewWindow(boolean newWindow) { - this.newWindow = newWindow; - } - - public boolean isNewWindow() { - return newWindow; - } + /* @Nullable */ + private static Configuration instance; + private static final Object instanceLock = new Object(); + + private static final String CONFIG_FILE = "config.properties"; + private static final String CONTENT_HANDLER_HANDLE = "resource/tle-resource"; + + // DEPRECATED CONFIG ELEMENTS + private static final String HOST = "host"; + private static final String PORT = "port"; + private static final String CONTEXT = "context"; + private static final String INSTITUTION = "institution"; + private static final String SECURE = "secure"; + private static final String MOCK_PORTAL_ROLES = "mock.portal.roles"; + + // CONFIG ELEMENTS (referred to in the JSP) + public static final String EQUELLA_URL = "equellaurl"; + public static final String SECRET = "secret"; + public static final String SECRETID = "secretid"; + public static final String OAUTH_CLIENT_ID = "oauth.clientid"; + public static final String OAUTH_CLIENT_SECRET = "oauth.clientsecret"; + public static final String RESTRICTIONS = "restrictions"; + public static final String LOG_LEVEL = "loglevel"; + public static final String NEWWINDOW = "newwindow"; + + private static final String DEFAULT_SECRET = ""; + private static final String DEFAULT_RESTRICTION = "none"; + private static final LogLevel DEFAULT_LOG_LEVEL = BbLogger.LogLevel.Info; + + private final BbContext context; + private File configDirectory; + /* @Nullable */ + private ContentHandler contentHandler; + private final Object contentHandlerLock = new Object(); + + /* @Nullable */ + private String version; + /* @Nullable */ + private String equellaUrl; + /* @Nullable */ + private String secret; + /* @Nullable */ + private String secretid; + /* @Nullable */ + private String oauthClientId; + /* @Nullable */ + private String oauthClientSecret; + /* @Nullable */ + private String restriction; + /* @Nullable */ + private String logLevel; + private boolean newWindow; + /* @Nullable */ + private Set mockPortalRoles; + + private Date lastModified = Calendar.getInstance().getTime(); + + public static Configuration instance() { + if (instance == null) { + synchronized (instanceLock) { + if (instance == null) { + instance = new Configuration(); + } + } + } + return instance; + } + + private Configuration() { + try { + configDirectory = PlugInUtil.getConfigDirectory(BbUtil.VENDOR, BbUtil.HANDLE); + + context = BbContext.instance(); + + final VirtualInstallationManager vim = VirtualInstallationManagerFactory.getInstance(); + final VirtualHost vhost = vim.getVirtualHost(""); + context.getContextManager().setContext(vhost); + + version = loadVersion(); + BbLogger.instance().logTrace("Version: " + version); + + load(); + ensureLtiPlacement(); + ensureScoreProvider(); + // Fix up dodgy Blind SSL + // HttpsURLConnection.setDefaultSSLSocketFactory(new + // sun.security.ssl.SSLSocketFactoryImpl()); + } catch (PlugInException e) { + BbLogger.instance().logError( + "Unable to obtain the plugin config directory for " + BbUtil.VENDOR + "-" + BbUtil.HANDLE, e); + throw new RuntimeException(e); + } catch (Exception e) { + BbLogger.instance().logError("Couldn't init building block", e); + throw new RuntimeException(e); + } + } + + @Override + protected void finalize() throws Throwable { + context.getContextManager().releaseContext(); + super.finalize(); + } + + public synchronized void modify(HttpServletRequest request) throws Exception { + setEquellaUrl(request.getParameter(EQUELLA_URL)); + setSecret(request.getParameter(SECRET)); + setSecretId(request.getParameter(SECRETID)); + setOauthClientId(request.getParameter(OAUTH_CLIENT_ID)); + setOauthClientSecret(request.getParameter(OAUTH_CLIENT_SECRET)); + setRestriction(request.getParameter(RESTRICTIONS)); + setLogLevel(request.getParameter(LOG_LEVEL)); + String newWindowParam = request.getParameter(NEWWINDOW); + if (newWindowParam == null || newWindowParam.equals("")) { + newWindowParam = "false"; + } + setNewWindow(Boolean.parseBoolean(newWindowParam)); + lastModified = Calendar.getInstance().getTime(); + + } + + public synchronized void load() { + final File configFile = new File(getConfigDirectory(), CONFIG_FILE); + if (!configFile.exists()) { + try { + configFile.createNewFile(); + BbLogger.instance().logTrace("Successfully created configuration file"); + } catch (IOException e) { + BbLogger.instance().logError("Error creating configuration file", e); + throw new RuntimeException(e); + } + return; + } + + FileInputStream fis = null; + try { + fis = new FileInputStream(configFile); + final Properties props = new Properties(); + props.load(fis); + if (props.containsKey(EQUELLA_URL)) { + setEquellaUrl(props.getProperty(EQUELLA_URL)); + } else { + try { + setEquellaUrl(buildEquellaUrlFromDeprecatedConfigParams(props)); + } catch (MalformedURLException mal) { + BbLogger.instance().logTrace("Failed to load equella URL from deprecated props"); + } + } + setSecret(props.getProperty(SECRET)); + setSecretId(props.getProperty(SECRETID)); + setOauthClientId(props.getProperty(OAUTH_CLIENT_ID)); + setOauthClientSecret(props.getProperty(OAUTH_CLIENT_SECRET)); + setMockPortalRoles(commaSplit(props.getProperty(MOCK_PORTAL_ROLES))); + setRestriction(props.getProperty(RESTRICTIONS)); + setLogLevel(props.getProperty(LOG_LEVEL)); + setNewWindow(Boolean.parseBoolean(props.getProperty(NEWWINDOW, "true"))); + } catch (Exception e) { + BbLogger.instance().logError("Error loading configuration", e); + throw new RuntimeException(e); + } finally { + if (fis != null) { + try { + fis.close(); + } catch (IOException e) { + // Ignore + } + } + } + } + + public synchronized void save() { + final File configFile = new File(getConfigDirectory(), CONFIG_FILE); + FileOutputStream fos = null; + try { + ensureLtiPlacement(); + ensureScoreProvider(); + + fos = new FileOutputStream(configFile); + + Properties props = new Properties(); + props.setProperty(EQUELLA_URL, equellaUrl); + props.setProperty(SECRET, secret); + props.setProperty(SECRETID, secretid); + props.setProperty(OAUTH_CLIENT_ID, oauthClientId); + props.setProperty(OAUTH_CLIENT_SECRET, oauthClientSecret); + props.setProperty(MOCK_PORTAL_ROLES, commaJoin(mockPortalRoles)); + props.setProperty(RESTRICTIONS, restriction); + props.setProperty(LOG_LEVEL, logLevel); + props.setProperty(NEWWINDOW, Boolean.toString(newWindow)); + props.store(fos, null); + } catch (Exception e) { + BbLogger.instance().logError("Error saving configuration", e); + throw new RuntimeException(e); + } finally { + if (fos != null) { + try { + fos.close(); + } catch (IOException e) { + // Ignore + } + } + } + } + + /** + * @Nullable + * @return + */ + private synchronized BasicLTIPlacement ensureLtiPlacement() { + try { + BbLogger.instance().logTrace("Entering ensureLtiPlacement"); + if (Strings.isNullOrEmpty(oauthClientId) || Strings.isNullOrEmpty(oauthClientSecret) + || Strings.isNullOrEmpty(equellaUrl)) { + BbLogger.instance().logTrace("Not creating a placement since a property was blank"); + return null; + } + + BasicLTIDomainConfig domainConfig = null; + boolean newPlacement = false; + BasicLTIPlacement placement = PlacementUtil.loadFromHandle(CONTENT_HANDLER_HANDLE); + if (placement == null) { + BbLogger.instance().logTrace("Loading placement via URL " + equellaUrl); + final LoadPlacementResponse loadPlacement = PlacementUtil.loadPlacementByUrl(equellaUrl); + placement = loadPlacement.getPlacement(); + domainConfig = loadPlacement.getDomainConfig(); + if (placement == null) { + BbLogger.instance().logTrace("No existing placement for URL " + equellaUrl); + final Id placementId = getContentHandler().getBasicLTIPlacementId(); + if (!Id.isValid(placementId)) { + BbLogger.instance().logTrace("No existing placement associated with ContentHandler"); + + if (domainConfig == null) { + domainConfig = createDomainConfig(); + } + placement = PlacementUtil.createNewPlacement(domainConfig, getContentHandler()); + newPlacement = true; + } else { + BbLogger.instance().logTrace("Loading existing placement from ContentHandler"); + placement = PlacementUtil.loadFromId(placementId); + if (placement == null) { + BbLogger.instance().logTrace("Content handler pointing to invalid placement..."); + if (domainConfig == null) { + domainConfig = createDomainConfig(); + } + placement = PlacementUtil.createNewPlacement(domainConfig, getContentHandler()); + newPlacement = true; + } + } + } else { + BbLogger.instance().logTrace("Loaded placement via URL"); + } + } else { + BbLogger.instance().logTrace("Loaded placement via handle"); + } + + // ensure domainConfig + if (domainConfig == null) { + BbLogger.instance().logTrace("No domain config loaded"); + + // get any current domain config for this domain + domainConfig = PlacementUtil.loadDomainConfigByUrl(new URL(equellaUrl)); + + if (domainConfig != null) { + BbLogger.instance().logTrace("Domain config loaded via URL " + equellaUrl); + if (populateDomainConfig(domainConfig)) { + BbLogger.instance().logTrace("Saving dirty domain config"); + final BasicLTIDomainConfigManager fac = BasicLTIDomainConfigManagerFactory.getInstance(); + fac.save(domainConfig); + } else { + BbLogger.instance().logTrace("Not saving doman config (not dirty)"); + } + } else { + BbLogger.instance().logTrace("No domain config for URL " + equellaUrl); + final Id domainConfigId = placement.getBasicLTIDomainConfigId(); + if (Id.isValid(domainConfigId)) { + BbLogger.instance().logTrace("Loading domain config based on placement pointer"); + final BasicLTIDomainConfigManager fac = BasicLTIDomainConfigManagerFactory.getInstance(); + domainConfig = fac.loadById(domainConfigId); + if (populateDomainConfig(domainConfig)) { + BbLogger.instance().logTrace("Saving dirty domain config"); + fac.save(domainConfig); + } else { + BbLogger.instance().logTrace("Not saving domain config (not dirty)"); + } + } else { + BbLogger.instance().logTrace("Creating new domain config"); + domainConfig = createDomainConfig(); + } + } + } + + BbLogger.instance().logTrace("newPlacement = " + newPlacement); + BbLogger.instance().logTrace("placement = " + placement.getId().toExternalString()); + + boolean saveHandler = newPlacement; + // delete the old placement associated with this handler if + // there is one + final ContentHandler handler = getContentHandler(); + if (handlerPlacementMismatch(placement, handler)) { + if (Id.isValid(handler.getBasicLTIPlacementId())) { + BbLogger.instance().logTrace("Deleting existing placement associated with this handler " + + handler.getBasicLTIPlacementId().toExternalString()); + PlacementUtil.deleteById(handler.getBasicLTIPlacementId()); + } + saveHandler = true; + } + + // Save placement? + // if( newPlacement || requiresSave(placement, domainConfig) ) + // { + placement.setBasicLTIDomainConfigId(domainConfig.getId()); + placement.setUrl(equellaUrl); + PlacementUtil.save(placement); + // } + + // Save handler? + if (saveHandler) { + BbLogger.instance().logTrace( + "Saving updated ContentHandler with placement " + placement.getId().toExternalString()); + + handler.setBasicLTIPlacementId(placement.getId()); + final ContentHandlerDbPersister contentHandlerPersister = (ContentHandlerDbPersister) BbContext + .instance().getPersistenceManager().getPersister(ContentHandlerDbPersister.TYPE); + contentHandlerPersister.persist(handler); + } else { + BbLogger.instance().logTrace("Not saving ContentHandler"); + } + + return placement; + } catch (Exception e) { + BbLogger.instance().logError("Error ensuring LTI placement", e); + throw new RuntimeException(e); + } + } + + private boolean handlerPlacementMismatch(BasicLTIPlacement placement, ContentHandler handler) { + final Id currentPlacementId = handler.getBasicLTIPlacementId(); + if (!currentPlacementId.equals(placement.getId())) { + BbLogger.instance().logTrace("ContentHandler is pointing at a different (or null) placement " + + handler.getBasicLTIPlacementId().toExternalString()); + return true; + } + BbLogger.instance().logTrace("ContentHandler is pointing to correct placement"); + return false; + } + + private synchronized ScoreProvider ensureScoreProvider() { + final ScoreProviderDAO dao = ScoreProviderDAO.get(); + ScoreProvider provider = dao.getByHandle(BbUtil.CONTENT_HANDLER); + if (provider == null) { + // The boolean values are cloned from what I could find about + // resource/x-bb-blti-link + // in score_provider.txt in BB installer + provider = new ScoreProvider(); + provider.setName("Equella"); + provider.setHandle(BbUtil.CONTENT_HANDLER); + provider.setAllowAttemptGrading(true); + provider.setAllowMultiple(false); + provider.setAttemptBased(false); + provider.setGradeAction(PathUtils.urlPath(BbUtil.getBlockRelativePath(), "ViewGradebook")); + provider.setReviewAction(PathUtils.urlPath(BbUtil.getBlockRelativePath(), "ViewGradebook")); + dao.persist(provider); + } + + return provider; + } + + private BasicLTIDomainConfig createDomainConfig() { + final BasicLTIDomainConfig domainConfig = new BasicLTIDomainConfig(); + populateDomainConfig(domainConfig); + domainConfig.setSendEmail(true); + domainConfig.setSendName(true); + domainConfig.setSendRole(true); + domainConfig.setUseSplash(false); + domainConfig.setSendUserData(SendUserData.Always); + domainConfig.setStatus(Status.Approved); + final BasicLTIDomainConfigManager fac = BasicLTIDomainConfigManagerFactory.getInstance(); + fac.save(domainConfig); + return domainConfig; + } + + /** + * @param domainConfig + * @return Was changed + */ + private boolean populateDomainConfig(BasicLTIDomainConfig domainConfig) { + try { + boolean dirty = false; + final String key = domainConfig.getKey(); + if (!Strings.nullToEmpty(key).equals(Strings.nullToEmpty(oauthClientId))) { + domainConfig.setKey(oauthClientId); + dirty = true; + } + final String secret = domainConfig.getSecret(); + if (!Strings.nullToEmpty(secret).equals(Strings.nullToEmpty(oauthClientSecret))) { + domainConfig.setSecret(oauthClientSecret); + dirty = true; + } + + BasicLTIDomainHost host = domainConfig.getPrimaryHost(); + final String newDomain = new URL(equellaUrl).getHost(); + if (host == null || !Strings.nullToEmpty(host.getDomain()).equals(Strings.nullToEmpty(newDomain))) { + host = (host == null ? new BasicLTIDomainHost() : host); + host.setDomain(newDomain); + host.setPrimary(true); + domainConfig.setPrimaryHost(host); + dirty = true; + } + return dirty; + } catch (Exception mal) { + BbLogger.instance().logError("Error populating domainConfig", mal); + throw new RuntimeException(mal); + } + } + + private ContentHandler getContentHandler() throws Exception { + if (contentHandler == null) { + synchronized (contentHandlerLock) { + if (contentHandler == null) { + final ContentHandlerDbLoader contentHandlerLoader = (ContentHandlerDbLoader) BbContext.instance() + .getPersistenceManager().getLoader(ContentHandlerDbLoader.TYPE); + contentHandler = contentHandlerLoader.loadByHandle(CONTENT_HANDLER_HANDLE); + final Id basicLTIPlacementId = contentHandler.getBasicLTIPlacementId(); + BbLogger.instance().logTrace("Loaded content handler from DB, placement = " + + (basicLTIPlacementId == null ? "null" : basicLTIPlacementId.toExternalString())); + } + } + } + return contentHandler; + } + + public static String loadVersion() throws IOException { + InputStream in = null; + try { + // CBEACH: This file wasn't able to be read. maybe a permissions or location + // issue. + // final Properties p = new Properties(); + // in = Configuration.class.getResourceAsStream("/version.properties"); + // p.load(in); + // final String versionB2 = p.getProperty("version.b2"); + // return versionB2; + BbLogger.instance().logTrace("Loading Version."); + return "TODO - need to implement version from file system property."; + } catch (Exception e) { + BbLogger.instance().logError("Couldn't load version", e); + throw new RuntimeException(e); + } finally { + Closeables.close(in, false); + } + } + + public File getConfigDirectory() { + return configDirectory; + } + + private String buildEquellaUrlFromDeprecatedConfigParams(Properties props) throws MalformedURLException { + final boolean secure = Boolean.valueOf(props.getProperty(SECURE)); + final String host = props.getProperty(HOST, "localhost"); + final int port = Integer.parseInt(props.getProperty(PORT, "80")); + final String context = props.getProperty(CONTEXT, "/"); + final String inst = props.getProperty(INSTITUTION, ""); + + return new URL(new URL(secure ? "https" : "http", host, port, context), inst).toString(); + } + + public boolean hasBeenModified(Date lastUpdate) { + return lastUpdate.before(lastModified); + } + + private Set commaSplit(/* @Nullable */String value) { + final Set result = new HashSet(); + if (value != null) { + final String[] vs = value.split(","); + for (int i = 0; i < vs.length; i++) { + result.add(vs[i].trim()); + } + } + return result; + } + + private String commaJoin(/* @Nullable */Collection values) { + final StringBuilder roles = new StringBuilder(); + if (values != null) { + for (Iterator iter = values.iterator(); iter.hasNext();) { + if (roles.length() > 0) { + roles.append(','); + } + roles.append(iter.next()); + } + } + return roles.toString(); + } + + /* @Nullable */ + public String getEquellaUrl() { + return equellaUrl; + } + + public void setEquellaUrl(/* @Nullable */String equellaUrl) { + this.equellaUrl = (equellaUrl == null ? null : equellaUrl.trim()); + if (!Strings.isNullOrEmpty(this.equellaUrl) && !this.equellaUrl.endsWith("/")) { + this.equellaUrl += '/'; + } + } + + /* @Nullable */ + public String getSecret() { + return secret; + } + + public void setSecret(/* @Nullable */String secret) { + if (secret == null || secret.length() == 0) { + this.secret = DEFAULT_SECRET; + } else { + this.secret = secret; + } + } + + /* @Nullable */ + public String getSecretId() { + return secretid; + } + + public void setSecretId(/* @Nullable */String secretid) { + if (secretid == null || secretid.length() == 0) { + this.secretid = DEFAULT_SECRET; + } else { + this.secretid = secretid; + } + } + + /* @Nullable */ + public String getOauthClientId() { + return oauthClientId; + } + + public void setOauthClientId(/* @Nullable */String oauthClientId) { + this.oauthClientId = oauthClientId; + } + + /* @Nullable */ + public String getOauthClientSecret() { + return oauthClientSecret; + } + + public void setOauthClientSecret(/* @Nullable */String oauthClientSecret) { + this.oauthClientSecret = oauthClientSecret; + } + + /* @Nullable */ + public String getVersion() { + return version; + } + + public void setMockPortalRoles(/* @Nullable */Set mockPortalRoles) { + this.mockPortalRoles = mockPortalRoles; + } + + /* @Nullable */ + public Set getMockPortalRoles() { + return mockPortalRoles; + } + + /* @Nullable */ + public String getRestriction() { + return restriction; + } + + public void setRestriction(/* @Nullable */String restriction) { + if (restriction == null) { + this.restriction = DEFAULT_RESTRICTION; + } else { + this.restriction = restriction; + } + } + + /* @Nullable */ + public String getLogLevel() { + return logLevel; + } + + public void setLogLevel(/* @Nullable */String level) { + if (level == null) { + this.logLevel = DEFAULT_LOG_LEVEL.name(); + } else { + this.logLevel = level; + } + BbLogger.instance().setLoggingLevel(LogLevel.valueOf(this.logLevel)); + } + + public void setNewWindow(boolean newWindow) { + this.newWindow = newWindow; + } + + public boolean isNewWindow() { + return newWindow; + } } \ No newline at end of file diff --git a/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/data/WrappedContent.java b/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/data/WrappedContent.java index f206af3..9c02e3b 100644 --- a/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/data/WrappedContent.java +++ b/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/data/WrappedContent.java @@ -444,7 +444,6 @@ public void modify(HttpServletRequest request) throws Exception { BbLogger.instance().logTrace("displayUntilString=" + displayUntilString); BbLogger.instance().logTrace("newWindowString=" + newWindowString); - // Kill legacy shiite. killLegacyFields(); setTitle(name); diff --git a/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/data/WrappedUser.java b/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/data/WrappedUser.java index c213c6c..e90b717 100644 --- a/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/data/WrappedUser.java +++ b/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/data/WrappedUser.java @@ -284,19 +284,31 @@ public static WrappedUser getUser(HttpServletRequest request) { WrappedUser suser = null; try { suser = (WrappedUser) request.getSession().getAttribute("TLE_USER"); - + String username = "user is null"; + if (suser != null) + username = suser.getUsername(); + BbLogger.instance() + .logWarn("oeqIntegStatelessIssue - getUser requesting TLE_USER from session. Received: " + username); // Have probably been logged out... if (suser != null && !user.getId().equals(suser.getId())) { + BbLogger.instance() + .logWarn("oeqIntegStatelessIssue - getUser mismatched user IDs. " + "session=[ID=" + + suser.getId().getExternalString() + ", username=" + suser.getId().getExternalString() + "], context=" + + "[ID=" + suser.getId().getExternalString() + ", username=" + suser.getId().getExternalString() + "]"); suser = null; } } catch (final Exception cce) { suser = null; // When BB gets redeployed this will happen + BbLogger.instance().logWarn("oeqIntegStatelessIssue - Attempted to getUser but session TLE_USER was null."); + } if (suser == null) { suser = user; request.getSession().setAttribute("TLE_USER", user); + BbLogger.instance() + .logWarn("oeqIntegStatelessIssue - getUser setting TLE_USER from session as " + user.getUsername()); } return suser; } diff --git a/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/lti/FixedBasicLtiLauncher.java b/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/lti/FixedBasicLtiLauncher.java index 7fc8995..358ce30 100644 --- a/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/lti/FixedBasicLtiLauncher.java +++ b/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/lti/FixedBasicLtiLauncher.java @@ -25,6 +25,7 @@ import org.apereo.openequella.integration.blackboard.buildingblock.Configuration; import org.apereo.openequella.integration.blackboard.buildingblock.data.WrappedContent; import org.apereo.openequella.integration.blackboard.buildingblock.data.WrappedUser; +import org.apereo.openequella.integration.blackboard.common.BbLogger; import org.apereo.openequella.integration.blackboard.common.BbUtil; import org.apereo.openequella.integration.blackboard.common.PathUtils; @@ -38,157 +39,160 @@ @SuppressWarnings("nls") // @NonNullByDefault public class FixedBasicLtiLauncher extends BasicLTILauncher { - // Sigh, BasicLTILauncher._parameters is private in SP11 - private final Map params2; - private String ourUrl; - - private FixedBasicLtiLauncher(String fullUrl, String clientId, String clientSecret, /* - * @ Nullable - */ - String resourceLinkId) { - super(fullUrl, clientId, clientSecret, resourceLinkId); - - ourUrl = fullUrl; - - // Ugh, _parameters is protected in SP13 but *private* in SP11 - try { - final Field paramsField = BasicLTILauncher.class.getDeclaredField("_parameters"); - paramsField.setAccessible(true); - params2 = (Map) paramsField.get(this); - } catch (Exception e) { - throw new RuntimeException(e); - } - - // Put the query string values into the parameters (does this double - // post?) - final int question = fullUrl.indexOf('?'); - if (question > 0) { - final String qs = fullUrl.substring(question + 1); - - final String[] params = qs.split("&"); - for (String param : params) { - final String[] nameVal = param.split("="); - if (nameVal.length == 2) { - params2.put(decodePercent(nameVal[0]), decodePercent(nameVal[1])); - } else { - params2.put(decodePercent(nameVal[0]), null); - } - } - } - } - - private String decodePercent(/* @Nullable */String s) { - if (s == null) { - return ""; - } - try { - return URLDecoder.decode(s, "UTF-8"); - } catch (UnsupportedEncodingException wow) { - throw new RuntimeException(wow.getMessage(), wow); - } - } - - @Override - public FixedBasicLtiLauncher addResourceLinkInformation(/* @Nullable */String title, /* @Nullable */ - String description) { - // If a description contains a newline or a br tag then Blackboard - // doesn't sign correctly. - // We don't use it anyway, so stop sending it. - final String usedDescription = ""; - // If a title contains certain special characters, such as ߢ , - // then Blackboard doesn't sign correctly. - // QTI is the only functionality using the title, and should default to - // the 'local' Equella title for the item when this title is empty. - final String usedTitle = ""; - return (FixedBasicLtiLauncher) super.addResourceLinkInformation(usedTitle, usedDescription); - } - - public FixedBasicLtiLauncher addGradingInformation(HttpServletRequest request, WrappedContent content) { - WrappedUser user = null; - try { - user = WrappedUser.getUser(request); - final Id courseId = BbUtil.getCourseId(request.getParameter(COURSE_ID)); - try { - final CourseMembership courseMembership = user.getCourseMembership(courseId); - return (FixedBasicLtiLauncher) super.addGradingInformation(content.getContent(), courseMembership); - } catch (KeyNotFoundException knf) { - // No enrollment, don't add anything - return this; - } - } catch (Exception e) { - throw Throwables.propagate(e); - } finally { - if (user != null) { - user.clearContext(); - } - } - } - - @Override - public BasicLTILauncher addCurrentUserInformation(boolean includeRoles, boolean includeName, boolean includeEmail, - IdTypeToSend idTypeToSend) { - // add username - Context ctx = ContextManagerFactory.getInstance().getContext(); - User user = ctx.getUser(); - addPostData("lis_person_sourcedid", user.getUserName()); - - return super.addCurrentUserInformation(includeRoles, includeName, includeEmail, idTypeToSend); - } - - @Override - public BasicLTILauncher addUserInformation(User user, CourseMembership membership, boolean includeRoles, - boolean includeName, boolean includeEmail, IdTypeToSend idTypeToSend) { - addPostData("lis_person_sourcedid", user.getUserName()); - return super.addUserInformation(user, membership, includeRoles, includeName, includeEmail, idTypeToSend); - } - - public FixedBasicLtiLauncher addPostData(String key, /* @Nullable */String value) { - params2.put(key, value); - return this; - } - - public FixedBasicLtiLauncher addReturnUrl(String returnUrl) { - final Map launchPresentation = new HashMap(); - launchPresentation.put(BasicLTIConstants.PARAM_LAUNCH_PRESENTATION_DOCUMENT_TARGET, - BasicLTIConstants.PARAM_LAUNCH_PRESENTATION_TARGET_FRAME); - launchPresentation.put(BasicLTIConstants.PARAM_LAUNCH_PRESENTATION_RETURN_URL, returnUrl); - return (FixedBasicLtiLauncher) super.addLaunchPresentationInformation(launchPresentation); - } - - @Override - public void launch(HttpServletRequest request, HttpServletResponse response, boolean useSplashScreen, - FormattedText splashScreenMessage) { - try { - Map params = prepareParameters(); - - request.setAttribute("toolUrl", ourUrl); - request.setAttribute("bltiParams", params); - request.setAttribute("useSplashScreen", false); - request.setAttribute("splashScreenMessage", null); - request.setAttribute("shouldOpenInFrame", true); - request.getServletContext().getContext("/webapps/blackboard").getRequestDispatcher("/blti/launch.jsp") - .forward(request, response); - } catch (RuntimeException re) { - throw re; - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - public static FixedBasicLtiLauncher newLauncher(Configuration configuration, String urlPath, - /* @Nullable */String resourceLinkId) { - final boolean appendSlash = urlPath.endsWith("/"); - final String url = PathUtils.urlPath(configuration.getEquellaUrl(), urlPath); - final String clientId = configuration.getOauthClientId(); - final String clientSecret = configuration.getOauthClientSecret(); - - if (url == null || clientId == null || clientSecret == null) { - throw new RuntimeException( - "One of URL, clientId or clientSecret not configured: " + url + ", " + clientId + ", " + clientSecret); - } - - final FixedBasicLtiLauncher launcher = new FixedBasicLtiLauncher(appendSlash ? url + "/" : url, clientId, - clientSecret, resourceLinkId); - return launcher; - } + // Sigh, BasicLTILauncher._parameters is private in SP11 + private final Map params2; + private String ourUrl; + + private FixedBasicLtiLauncher(String fullUrl, String clientId, String clientSecret, /* + * @ Nullable + */ + String resourceLinkId) { + super(fullUrl, clientId, clientSecret, resourceLinkId); + + ourUrl = fullUrl; + + // Ugh, _parameters is protected in SP13 but *private* in SP11 + try { + final Field paramsField = BasicLTILauncher.class.getDeclaredField("_parameters"); + paramsField.setAccessible(true); + params2 = (Map) paramsField.get(this); + } catch (Exception e) { + throw new RuntimeException(e); + } + + // Put the query string values into the parameters (does this double + // post?) + final int question = fullUrl.indexOf('?'); + if (question > 0) { + final String qs = fullUrl.substring(question + 1); + + final String[] params = qs.split("&"); + for (String param : params) { + final String[] nameVal = param.split("="); + if (nameVal.length == 2) { + params2.put(decodePercent(nameVal[0]), decodePercent(nameVal[1])); + } else { + params2.put(decodePercent(nameVal[0]), null); + } + } + } + } + + private String decodePercent(/* @Nullable */String s) { + if (s == null) { + return ""; + } + try { + return URLDecoder.decode(s, "UTF-8"); + } catch (UnsupportedEncodingException wow) { + throw new RuntimeException(wow.getMessage(), wow); + } + } + + @Override + public FixedBasicLtiLauncher addResourceLinkInformation(/* @Nullable */String title, /* @Nullable */ + String description) { + // If a description contains a newline or a br tag then Blackboard + // doesn't sign correctly. + // We don't use it anyway, so stop sending it. + final String usedDescription = ""; + // If a title contains certain special characters, such as ߢ , + // then Blackboard doesn't sign correctly. + // QTI is the only functionality using the title, and should default to + // the 'local' Equella title for the item when this title is empty. + final String usedTitle = ""; + return (FixedBasicLtiLauncher) super.addResourceLinkInformation(usedTitle, usedDescription); + } + + public FixedBasicLtiLauncher addGradingInformation(HttpServletRequest request, WrappedContent content) { + WrappedUser user = null; + try { + user = WrappedUser.getUser(request); + final Id courseId = BbUtil.getCourseId(request.getParameter(COURSE_ID)); + try { + final CourseMembership courseMembership = user.getCourseMembership(courseId); + return (FixedBasicLtiLauncher) super.addGradingInformation(content.getContent(), courseMembership); + } catch (KeyNotFoundException knf) { + // No enrollment, don't add anything + return this; + } + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + if (user != null) { + user.clearContext(); + } + } + } + + @Override + public BasicLTILauncher addCurrentUserInformation(boolean includeRoles, boolean includeName, boolean includeEmail, + IdTypeToSend idTypeToSend) { + // add username + Context ctx = ContextManagerFactory.getInstance().getContext(); + User user = ctx.getUser(); + addPostData("lis_person_sourcedid", user.getUserName()); + + return super.addCurrentUserInformation(includeRoles, includeName, includeEmail, idTypeToSend); + } + + @Override + public BasicLTILauncher addUserInformation(User user, CourseMembership membership, boolean includeRoles, + boolean includeName, boolean includeEmail, IdTypeToSend idTypeToSend) { + addPostData("lis_person_sourcedid", user.getUserName()); + return super.addUserInformation(user, membership, includeRoles, includeName, includeEmail, idTypeToSend); + } + + public FixedBasicLtiLauncher addPostData(String key, /* @Nullable */String value) { + params2.put(key, value); + return this; + } + + public FixedBasicLtiLauncher addReturnUrl(String returnUrl) { + final Map launchPresentation = new HashMap(); + launchPresentation.put(BasicLTIConstants.PARAM_LAUNCH_PRESENTATION_DOCUMENT_TARGET, + BasicLTIConstants.PARAM_LAUNCH_PRESENTATION_TARGET_FRAME); + launchPresentation.put(BasicLTIConstants.PARAM_LAUNCH_PRESENTATION_RETURN_URL, returnUrl); + return (FixedBasicLtiLauncher) super.addLaunchPresentationInformation(launchPresentation); + } + + @Override + public void launch(HttpServletRequest request, HttpServletResponse response, boolean useSplashScreen, + FormattedText splashScreenMessage) { + try { + Map params = prepareParameters(); + + request.setAttribute("toolUrl", ourUrl); + request.setAttribute("bltiParams", params); + request.setAttribute("useSplashScreen", false); + request.setAttribute("splashScreenMessage", null); + request.setAttribute("shouldOpenInFrame", true); + BbLogger.instance().logTrace( + "FixedBasicLtiLauncher.launch: Calling parent launch with url=[" + ourUrl + "], params=[(see below)]", + params); + request.getServletContext().getContext("/webapps/blackboard").getRequestDispatcher("/blti/launch.jsp") + .forward(request, response); + } catch (RuntimeException re) { + throw re; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static FixedBasicLtiLauncher newLauncher(Configuration configuration, String urlPath, + /* @Nullable */String resourceLinkId) { + final boolean appendSlash = urlPath.endsWith("/"); + final String url = PathUtils.urlPath(configuration.getEquellaUrl(), urlPath); + final String clientId = configuration.getOauthClientId(); + final String clientSecret = configuration.getOauthClientSecret(); + + if (url == null || clientId == null || clientSecret == null) { + throw new RuntimeException( + "One of URL, clientId or clientSecret not configured: " + url + ", " + clientId + ", " + clientSecret); + } + + final FixedBasicLtiLauncher launcher = new FixedBasicLtiLauncher(appendSlash ? url + "/" : url, clientId, + clientSecret, resourceLinkId); + return launcher; + } } diff --git a/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/servlet/ContentServlet.java b/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/servlet/ContentServlet.java index 6cf4467..8afb1cc 100644 --- a/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/servlet/ContentServlet.java +++ b/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/servlet/ContentServlet.java @@ -14,25 +14,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import blackboard.data.ExtendedData; -import blackboard.data.content.Content; -import blackboard.data.course.Course; -import blackboard.data.course.CourseMembership; -import blackboard.data.gradebook.impl.OutcomeDefinition; -import blackboard.persist.Id; -import blackboard.persist.content.ContentDbLoader; -import blackboard.persist.course.CourseMembershipDbLoader; -import blackboard.persist.gradebook.impl.OutcomeDefinitionDbLoader; -import blackboard.platform.ContentWrapperHelper; -import blackboard.platform.blti.BasicLTILauncher.IdTypeToSend; -import blackboard.platform.plugin.PlugInUtil; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.google.common.base.Strings; -import com.google.common.base.Throwables; import org.apereo.openequella.integration.blackboard.buildingblock.BlockUtil; import org.apereo.openequella.integration.blackboard.buildingblock.Configuration; import org.apereo.openequella.integration.blackboard.buildingblock.data.WrappedContent; @@ -47,6 +28,26 @@ import org.apereo.openequella.integration.blackboard.common.content.ItemUtil; import org.apereo.openequella.integration.blackboard.common.content.LegacyItemUtil; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.base.Strings; +import com.google.common.base.Throwables; + +import blackboard.data.ExtendedData; +import blackboard.data.content.Content; +import blackboard.data.course.Course; +import blackboard.data.course.CourseMembership; +import blackboard.data.gradebook.impl.OutcomeDefinition; +import blackboard.persist.Id; +import blackboard.persist.content.ContentDbLoader; +import blackboard.persist.course.CourseMembershipDbLoader; +import blackboard.persist.gradebook.impl.OutcomeDefinitionDbLoader; +import blackboard.platform.ContentWrapperHelper; +import blackboard.platform.blti.BasicLTILauncher.IdTypeToSend; +import blackboard.platform.plugin.PlugInUtil; + @SuppressWarnings("nls") public class ContentServlet extends HttpServlet { private static final String COURSE_ID = "course_id"; @@ -180,7 +181,7 @@ public static void forwardToContentWrapper(HttpServletRequest request, HttpServl // response.sendRedirect(redirect.toString()); } catch (final Exception e) { BbLogger.instance().logError("Error in ViewContent", e); - throw Throwables.propagate(e); + throw new RuntimeException(e); } } @@ -223,7 +224,11 @@ private void viewContentBody(HttpServletRequest request, HttpServletResponse res final FixedBasicLtiLauncher launcher = FixedBasicLtiLauncher.newLauncher(configuration, launchUrl, contentId.toExternalString()); + BbLogger.instance() + .logTrace("viewContentBody - created launcher. URL=[" + launchUrl + "] and contentId=[" + contentId + "]"); + launcher.addResourceLinkInformation(name, description); + BbLogger.instance().logTrace("viewContentBody - added name=[" + name + "] and description=[" + description + "]"); // Check course membership as supplied from the gradebook final CourseMembership membership = getCourseMembershipFromRequest(request); @@ -231,16 +236,23 @@ private void viewContentBody(HttpServletRequest request, HttpServletResponse res launcher.addGradingInformation(request, content); launcher.addCurrentUserInformation(true, true, true, IdTypeToSend.PK1); } else { - launcher.addGradingInformation(bbContent, membership); + launcher.addGradingInformation(request, bbContent, membership); launcher.addUserInformation(membership.getUser(), membership, true, true, true, IdTypeToSend.PK1); } + BbLogger.instance().logTrace("viewContentBody - added grading and user info"); + BbLogger.instance().logTrace("viewContentBody - added current course information"); launcher.addCurrentCourseInformation(IdTypeToSend.PK1); - launcher.addReturnUrl(getReturnUrl(request, contentId, courseId)); + String rUrl = getReturnUrl(request, contentId, courseId); + BbLogger.instance().logTrace("viewContentBody - set return url to: " + rUrl); + launcher.addReturnUrl(rUrl); + BbLogger.instance().logTrace("viewContentBody - launching"); launcher.launch(request, response, false, null); + BbLogger.instance().logTrace("viewContentBody - launched content"); + } catch (final Exception e) { BbLogger.instance().logError("Error in ContentBody viewer", e); - throw Throwables.propagate(e); + throw new RuntimeException(e); } } @@ -423,7 +435,7 @@ private void addContent(HttpServletRequest request, HttpServletResponse response response.sendRedirect(contentReturnUrl); } catch (final Exception e) { BbLogger.instance().logError("Error in AddContent", e); - throw Throwables.propagate(e); + throw new RuntimeException(e); } finally { Thread.currentThread().setContextClassLoader(oldLoader); } diff --git a/oeqPrimaryB2/src/main/manifests/bb-manifest-unresolved.xml b/oeqPrimaryB2/src/main/manifests/bb-manifest-unresolved.xml index 5bdf202..c2a8f60 100644 --- a/oeqPrimaryB2/src/main/manifests/bb-manifest-unresolved.xml +++ b/oeqPrimaryB2/src/main/manifests/bb-manifest-unresolved.xml @@ -3,11 +3,11 @@ xsi:schemaLocation="http://www.blackboard.com/bb-manifest-plugin bb-manifest-plugin.xsd"> - + - + @@ -68,7 +68,7 @@ - + @@ -77,7 +77,7 @@ - + @@ -87,7 +87,7 @@ + value="Redirect?forward=access/myresources.do" /> @@ -110,9 +110,11 @@ - - + + + + diff --git a/oeqPrimaryB2/src/main/webapp/WEB-INF/web.xml b/oeqPrimaryB2/src/main/webapp/WEB-INF/web.xml index 288ddb7..dc5b982 100644 --- a/oeqPrimaryB2/src/main/webapp/WEB-INF/web.xml +++ b/oeqPrimaryB2/src/main/webapp/WEB-INF/web.xml @@ -1,20 +1,20 @@ - EQUELLA - EQUELLA Integration with Blackboard + openEQUELLA + openEQUELLA Integration with Blackboard Redirect - com.tle.blackboard.buildingblock.servlet.RedirectServlet + org.apereo.openequella.integration.blackboard.buildingblock.servlet.RedirectServlet Content - com.tle.blackboard.buildingblock.servlet.ContentServlet + org.apereo.openequella.integration.blackboard.buildingblock.servlet.ContentServlet " + + "" + + "
\"
" + + "" + + "
{1}
\"
" + + "
\"*\"  {6}
"; + + // @formatter:on + + public static String getHtml(String serverUrl, String xmlString) { + PropBagMin xml = new PropBagMin(xmlString); + + String attTitle = xml.getNode("attachments/@selectedTitle"); + String selectedPage = xml.getNode("attachments/@selected"); + String selectedAttachmentType = xml.getNode("attachments/@selectedType"); + String description = xml.getNode("description"); + String title = xml.getNode("name"); + String url = selectedPage; + + String fileForIcon = selectedPage; + if ("link".equals(selectedAttachmentType)) { + fileForIcon = "http://"; + } + + if (serverUrl.endsWith("/")) { + serverUrl = serverUrl.substring(0, serverUrl.length() - 1); + } + + final String html = MessageFormat.format(HTML_TEMPLATE, serverUrl, BbUtil.ent(description), + BbUtil.getBlockRelativePath(), BbUtil.ent(title), url, xml, BbUtil.ent(attTitle), + ItemUtil.getIcon(fileForIcon)); + return html; + } + + public static ItemInfo getItemInfo(Content content, Course course, Id folderId, String equellaUrl) { + String body = extractXmlFromBody(content.getBody().getFormattedText()); + final PropBagMin propBag = createPropBag(body); + return parseXml(propBag, content.getId().toExternalString(), course.getId().toExternalString(), + folderId.toExternalString(), equellaUrl); + } + + // Legacy items can have XML that is not always clean. This method covers + // one method of scrubbing the XML and allows future additions as clients + // need them. + protected static PropBagMin createPropBag(String body) { + try { + // Try with the original legacy XML + return new PropBagMin(body); + } catch (final Exception e) { + // That failed. Escape the '&' in the XML and try again + try { + String ampEscapedBody = fixUnescapedAmpersands(body); + return new PropBagMin(ampEscapedBody); + } catch (final Exception e2) { + // Possible future enhancement to escape entities such as   + // or &blahblah; - for now, fail with an error. + LOGGER.logError("Error parsing body of item \n" + body, e2); + throw new RuntimeException(e2); + } + } + } + + // Due to EQ-2260. When &'s are in the item body xml, Equella 6.2+ building + // block fails. Maybe not 100% effective, but should handle the vast + // majority of cases + protected static String fixUnescapedAmpersands(String original) { + final String regex = "&([^;\\W]*([^;\\w]|$))"; + final String replacement = "&$1"; + // The double replaceAll is to handle cases where there are multiple & + // together. + return original.replaceAll(regex, replacement).replaceAll(regex, replacement); + } + + // Due to EQ-2261. Legacy pages are not always ready to be directly called + // and need to be cleaned up + public static String scrubLegacyPage(String original) { + // Due to EQAS-403 / EQ-2306 + if ((original == null) || original.equals("./")) { + return ""; + } + + // When requesting pages that have folder structures in the item, + // don't scrub the path (it should be already clean) + int filenameIndex = original.lastIndexOf("/"); + if (filenameIndex != -1) { + return original; + } + + // There is no folder structure. Scrub the path. Note - Equella doesn't + // seem to like the escape character for space (+) - switch to %20 + return urlEncode(original).replaceAll("\\+", "%20"); + } + + public static String extractXmlFromBody(String body) { + if (body.length() == 0 || !body.startsWith(""); + return body.substring(4, temp); + } + + public static String extractUrlFromBody(String body) { + // This is grotesque. Maybe this should also be recorded in the usage + // table? + final Matcher matcher = PATTERN_URL.matcher(body); + matcher.matches(); + return matcher.group(1); + } + + private static ItemInfo parseXml(PropBagMin xml, String contentId, String courseId, String folderId, + String equellaUrl) { + if (xml.nodeExists("result")) { + xml = xml.getSubtree("result"); + } + if (xml.nodeExists("item") && !xml.getNodeName().equals("item")) { + xml = xml.getSubtree("item"); + } + + final ItemInfo itemInfo = new ItemInfo(equellaUrl, xml.getNode("@id"), xml.getIntNode("@version", 1), contentId, + courseId, folderId, xml.getNode("attachments/@selected")); + + itemInfo.setName(xml.getNode("name")); + itemInfo.setDescription(xml.getNode("description")); + itemInfo.setActivateRequestUuid(xml.getNode("requestUuid", null)); + final String selectedAttachmentType = xml.getNode("attachments/@selectedType"); + if (selectedAttachmentType.equals("link")) { + itemInfo.setMimeType("equella/link"); + } else { + itemInfo.setMimeType("application/octet-stream"); + } + + return itemInfo; + } + + public static String urlEncode(String url) { + try { + return URLEncoder.encode(url, "UTF-8"); + } catch (final UnsupportedEncodingException e) { + // Never happen + return url; + } + } +} \ No newline at end of file diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/PlacementUtil.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/PlacementUtil.java new file mode 100644 index 0000000..52df8cb --- /dev/null +++ b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/PlacementUtil.java @@ -0,0 +1,167 @@ +package org.apereo.openequella.integration.blackboard.common.content; + +import java.net.MalformedURLException; +import java.net.URL; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import blackboard.data.blti.BasicLTIDomainConfig; +import blackboard.data.blti.BasicLTIPlacement; +import blackboard.data.blti.BasicLTIPlacement.Type; +import blackboard.persist.Id; +import blackboard.persist.KeyNotFoundException; +import blackboard.platform.blti.BasicLTIDomainConfigManager; +import blackboard.platform.blti.BasicLTIDomainConfigManagerFactory; +import blackboard.platform.blti.BasicLTIPlacementManager; +import blackboard.platform.plugin.ContentHandler; +import blackboard.platform.plugin.ContentHandlerType.ActionType; + +import com.google.common.base.Throwables; + +import org.apereo.openequella.integration.blackboard.common.BbLogger; +import org.apereo.openequella.integration.blackboard.common.BbUtil; +import org.apereo.openequella.integration.blackboard.common.SqlUtil; +import org.apereo.openequella.integration.blackboard.common.SqlUtil.ResultProcessor; + +/** + * @author Aaron + */ +@SuppressWarnings("nls") +// @NonNullByDefault +public abstract class PlacementUtil { + private static final String LTI_PLACEMENT_TITLE = "openEQUELLA LTI Integration"; + + public PlacementUtil() { + throw new Error(); + } + + public static BasicLTIPlacement getDefaultPlacement() { + return PlacementUtil.loadFromHandle("resource/tle-resource"); + } + + /* @Nullable */ + public static BasicLTIPlacement loadFromHandle(String handle) { + final String sql = "SELECT pk1 FROM blti_placement WHERE handle=?"; + final List ids = SqlUtil.runSql(sql.toString(), new ResultProcessor() { + @Override + public List getResults(ResultSet results) throws SQLException { + final List idList = new ArrayList(); + while (results.next()) { + final Long contentId = results.getLong("pk1"); + final Id id = Id.toId(BasicLTIPlacement.DATA_TYPE, Long.toString(contentId)); + idList.add(id); + } + return idList; + } + }, handle); + if (ids.size() == 0) { + BbLogger.instance().logTrace("No placements found for handle " + handle); + return null; + } + BbLogger.instance().logTrace("Placements found for handle " + handle); + return loadFromId(ids.get(0)); + } + + /* @Nullable */ + public static BasicLTIPlacement loadFromId(Id placementId) { + final BasicLTIPlacementManager basicLTIPlacementManager = BasicLTIPlacementManager.Factory.getInstance(); + try { + return basicLTIPlacementManager.loadById(placementId, false); + } catch (KeyNotFoundException e) { + return null; + } + } + + public static LoadPlacementResponse loadPlacementByUrl(String urlString) throws Exception { + final LoadPlacementResponse response = new LoadPlacementResponse(); + // Handle badly formatted old URLs + final URL url; + try { + url = new URL(urlString); + } catch (MalformedURLException mal) { + return response; + } + + final BasicLTIDomainConfig domainConfig = loadDomainConfigByUrl(url); + response.setDomainConfig(domainConfig); + if (domainConfig != null) { + BbLogger.instance().logTrace("Existing BasicLTIDomainConfig, loading placements"); + final BasicLTIPlacementManager basicLTIPlacementManager = BasicLTIPlacementManager.Factory.getInstance(); + final List placements = basicLTIPlacementManager.loadByDomainConfigId(domainConfig.getId()); + if (placements == null || placements.size() == 0) { + BbLogger.instance().logTrace("No existing BasicLTIPlacements for domain config"); + } else { + // what if more than one? + BbLogger.instance() + .logTrace("Found " + placements.size() + " existing BasicLTIPlacements for host " + url.getHost()); + response.setPlacement(placements.get(0)); + } + } else { + BbLogger.instance().logTrace("No domain config for this URL"); + } + return response; + } + + /* @Nullable */ + public static BasicLTIDomainConfig loadDomainConfigByUrl(URL url) { + final BasicLTIDomainConfigManager fac = BasicLTIDomainConfigManagerFactory.getInstance(); + try { + return fac.loadByDomain(url.getHost()); + } catch (KeyNotFoundException knfUhHuhUhHuh) { + return null; + } + } + + public static BasicLTIPlacement createNewPlacement(BasicLTIDomainConfig domainConfig, ContentHandler contentHandler) { + BbLogger.instance().logTrace("Creating new BasicLTIPlacement"); + try { + final BasicLTIPlacement placement = new BasicLTIPlacement(); + placement.setType(Type.ContentHandler); + placement.setAllowGrading(true); + placement.setContentHandler(contentHandler); + placement.setName(LTI_PLACEMENT_TITLE); + placement.setHandle(contentHandler.getHandle()); + placement.setBasicLTIDomainConfigId(domainConfig.getId()); + return placement; + } catch (Exception e) { + BbLogger.instance().logError("Error creating placement", e); + throw Throwables.propagate(e); + } + } + + public static void deleteById(Id id) { + BbLogger.instance().logTrace("Deleting placement " + id.toExternalString()); + final BasicLTIPlacementManager basicLTIPlacementManager = BasicLTIPlacementManager.Factory.getInstance(); + basicLTIPlacementManager.deleteById(id); + } + + public static void save(BasicLTIPlacement placement) { + BbLogger.instance().logTrace("Saving placement " + placement.getId().toExternalString()); + final BasicLTIPlacementManager basicLTIPlacementManager = BasicLTIPlacementManager.Factory.getInstance(); + basicLTIPlacementManager.saveContentHandlerPlacement(placement, ActionType.none); + } + + // @NonNullByDefault(false) + public static class LoadPlacementResponse { + private BasicLTIPlacement placement; + private BasicLTIDomainConfig domainConfig; + + public BasicLTIPlacement getPlacement() { + return placement; + } + + public void setPlacement(BasicLTIPlacement placement) { + this.placement = placement; + } + + public BasicLTIDomainConfig getDomainConfig() { + return domainConfig; + } + + public void setDomainConfig(BasicLTIDomainConfig domainConfig) { + this.domainConfig = domainConfig; + } + } +} diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/RegistrationUtil.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/RegistrationUtil.java new file mode 100644 index 0000000..fa20069 --- /dev/null +++ b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/RegistrationUtil.java @@ -0,0 +1,555 @@ +package org.apereo.openequella.integration.blackboard.common.content; + +import static org.apereo.openequella.integration.blackboard.common.SqlUtil.delete; +import static org.apereo.openequella.integration.blackboard.common.SqlUtil.insert; +import static org.apereo.openequella.integration.blackboard.common.SqlUtil.select; +import static org.apereo.openequella.integration.blackboard.common.SqlUtil.update; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import blackboard.db.BbDatabase; +import blackboard.persist.DatabaseContainer; +import blackboard.platform.persistence.PersistenceServiceFactory; + +import com.google.common.base.Strings; + +import org.apereo.openequella.integration.blackboard.common.BbLogger; +import org.apereo.openequella.integration.blackboard.common.BbUtil; +import org.apereo.openequella.integration.blackboard.common.SqlUtil; +import org.apereo.openequella.integration.blackboard.common.SqlUtil.OptionalParam; +import org.apereo.openequella.integration.blackboard.common.SqlUtil.ResultProcessor; +import org.apereo.openequella.integration.blackboard.common.content.ContentUtil.ContentRegisteredResponse; + +/** + * @author Aaron + */ +@SuppressWarnings("nls") +// @NonNullByDefault +public abstract class RegistrationUtil { + // until proven otherwise. The databaseVersion is currently 5 (as of after 6.3 + // GA) + private static int databaseVersion = 0; + + private static final String COL_DATABASE_ID = "ID"; + private static final String COL_INSTITUTION_URL = "INSTURL"; + private static final String COL_DATE_CREATED = "DTCREATED"; + private static final String COL_DATE_MODIFIED = "DTMODIFIED"; + private static final String COL_UUID = "UUID"; + private static final String COL_VERSION = "VERSION"; + private static final String COL_PAGE = "PAGE"; + private static final String COL_CONTENT_ID = "CONTENTID"; + private static final String COL_BLACKBOARD_TITLE = "BBTITLE"; + private static final String COL_BLACKBOARD_DESC = "BBDESC"; + private static final String COL_COURSE_NAME = "COURSENAME"; + private static final String COL_COURSE_ID = "COURSEID"; + private static final String COL_FOLDER_ID = "FOLDERID"; + private static final String COL_AVAILABLE = "AVAILABLE"; + private static final String COL_COURSE_AVAILABLE = "COURSEAVAILABLE"; + private static final String COL_DATE_ACCESSED = "DTACCESSED"; + private static final String COL_ATTACHMENT_NAME = "ATTNAME"; + + public RegistrationUtil() { + throw new Error(); + } + + public static void recordItem(ItemKey itemKey, boolean added, boolean available, boolean courseAvailable, + String bbTitle, String bbDesc, String attachmentName, Date dateAdded, Date dateModifed, String courseName) { + BbLogger.instance() + .logTrace("recordItem(" + itemKey + ", " + added + ", " + available + ", " + courseAvailable + ", " + bbTitle + + ", " + bbDesc + ", " + attachmentName + ", " + dateAdded + ", " + dateModifed + ", " + courseName + ")"); + ensureDatabase(); + + StringBuilder sql = null; + + final boolean oracle = getDatabase().isOracle(); + final String schema = SqlUtil.getSchema(oracle); + + final long now = System.currentTimeMillis(); + final java.sql.Timestamp addedTimestamp = new java.sql.Timestamp(dateAdded == null ? now : dateAdded.getTime()); + final java.sql.Timestamp modifiedTimestamp = new java.sql.Timestamp( + dateModifed == null ? now : dateModifed.getTime()); + + if (added) { + if (oracle) { + sql = insert(COL_DATABASE_ID, COL_DATE_CREATED, COL_DATE_MODIFIED, COL_INSTITUTION_URL, COL_UUID, COL_VERSION, + COL_COURSE_ID, COL_FOLDER_ID, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, + COL_BLACKBOARD_DESC, COL_PAGE, COL_CONTENT_ID, COL_COURSE_NAME, COL_ATTACHMENT_NAME).append(" VALUES (") + .append(schema).append("equellaseq.nextval, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ,?, ?, ?, ?)"); + } else { + sql = insert(COL_DATE_CREATED, COL_DATE_MODIFIED, COL_INSTITUTION_URL, COL_UUID, COL_VERSION, COL_COURSE_ID, + COL_FOLDER_ID, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC, COL_PAGE, + COL_CONTENT_ID, COL_COURSE_NAME, COL_ATTACHMENT_NAME) + .append(" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + } + SqlUtil.runSql(sql.toString(), null, addedTimestamp, modifiedTimestamp, itemKey.getInstitutionUrl(), + itemKey.getUuid(), itemKey.getVersion(), itemKey.getCourseId(), itemKey.getFolderId(), available, + courseAvailable, bbTitle, trim(bbDesc, 511), itemKey.getPage(), itemKey.getContentId(), courseName, + attachmentName); + itemKey.setDatabaseId(0); // would be nice to know what the new one + // is + } else { + String contentId = itemKey.getContentId(); + if (!Strings.isNullOrEmpty(contentId)) { + BbLogger.instance().logDebug("updating recorded usage using contentId"); + + sql = update(COL_DATE_MODIFIED, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC, + COL_PAGE, COL_COURSE_NAME, COL_ATTACHMENT_NAME).append("WHERE CONTENTID=?"); + List updateCount = SqlUtil.runSql(sql.toString(), null, modifiedTimestamp, available, courseAvailable, + bbTitle, bbDesc, itemKey.getPage(), courseName, attachmentName, contentId); + + int updates = updateCount.get(0); + if (updates > 1) { + // There have instances of dupes. Remove them all. This + // should be a rare occurrence + BbLogger.instance().logError("** duplicate recordings of " + contentId + " **", null); + + // remove dupes and add a brand new entry + unrecordItem(0, contentId); + recordItem(itemKey, true, available, courseAvailable, bbTitle, bbDesc, attachmentName, dateAdded, dateModifed, + courseName); + } else if (updates == 0) { + BbLogger.instance().logDebug("content ID not logged... updating the existing one and record the content ID"); + + sql = update(COL_DATE_MODIFIED, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, + COL_BLACKBOARD_DESC, COL_PAGE, COL_COURSE_NAME, COL_CONTENT_ID, COL_ATTACHMENT_NAME) + .append("WHERE INSTURL=? AND UUID=? AND VERSION=? AND COURSEID=? AND FOLDERID=?"); + updateCount = SqlUtil.runSql(sql.toString(), null, modifiedTimestamp, available, courseAvailable, bbTitle, + trim(bbDesc, 511), itemKey.getPage(), courseName, itemKey.getContentId(), attachmentName, + itemKey.getInstitutionUrl(), itemKey.getUuid(), itemKey.getVersion(), itemKey.getCourseId(), + itemKey.getFolderId()); + checkSingleUpdate(updateCount); + } + } else { + BbLogger.instance().logDebug("updating recorded usage using old field matching"); + + sql = update(COL_DATE_MODIFIED, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC, + COL_PAGE, COL_COURSE_NAME, COL_ATTACHMENT_NAME) + .append("WHERE INSTURL=? AND UUID=? AND VERSION=? AND COURSEID=? AND FOLDERID=?"); + final List updateCount = SqlUtil.runSql(sql.toString(), null, modifiedTimestamp, available, + courseAvailable, bbTitle, trim(bbDesc, 511), itemKey.getPage(), courseName, attachmentName, + itemKey.getInstitutionUrl(), itemKey.getUuid(), itemKey.getVersion(), itemKey.getCourseId(), + itemKey.getFolderId()); + checkSingleUpdate(updateCount); + } + } + } + + private static boolean checkSingleUpdate(List updateCount) { + final int updates = updateCount.get(0); + if (updates != 1) { + // houston, we have a problem + BbLogger.instance().logError("** Updated " + updates + " rows when only one should have been updated **", null); + return false; + } + return true; + } + + private static boolean unrecordItem(int usageId) { + BbLogger.instance().logDebug("unrecordItem(" + usageId + ")"); + ensureDatabase(); + + final List updateCount = SqlUtil.runSql(delete() + "WHERE ID=?", null, usageId); + return updateCount.get(0) > 0; + } + + public static boolean unrecordItem(int usageId, String contentId) { + BbLogger.instance().logDebug("unrecordItem(" + usageId + ", " + contentId + ")"); + ensureDatabase(); + + if (usageId != 0) { + boolean unrecorded = unrecordItem(usageId); + if (unrecorded) { + return true; + } + } + + final List updateCount = SqlUtil.runSql(delete() + "WHERE CONTENTID=?", null, contentId); + return updateCount.get(0) > 0; + } + + public static boolean updateRecordedTitle(String contentId, String title, String description) { + BbLogger.instance().logDebug("updateRecordedTitle(" + contentId + ", " + title + ", " + description + ")"); + ensureDatabase(); + + final List updateCount = SqlUtil.runSql( + update(COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC).append("WHERE CONTENTID=?").toString(), null, title, + description, contentId); + return updateCount.get(0) > 0; + } + + public static boolean updateRecordedLocation(String contentId, String courseId, String folderId) { + BbLogger.instance().logDebug("updateRecordedLocation(" + contentId + ", " + courseId + ", " + folderId + ")"); + ensureDatabase(); + + final List updateCount = SqlUtil.runSql( + update(COL_COURSE_ID, COL_FOLDER_ID).append("WHERE CONTENTID=?").toString(), null, courseId, folderId, + contentId); + return updateCount.get(0) > 0; + } + + public static boolean updateDateAccessed(String contentId, Date dateAccessed) { + BbLogger.instance().logDebug("updateDateAccessed(" + contentId + ", " + dateAccessed + ")"); + ensureDatabase(); + + final Timestamp accessedTimestamp = new Timestamp(dateAccessed.getTime()); + final List updateCount = SqlUtil.runSql(update(COL_DATE_ACCESSED).append("WHERE CONTENTID=?").toString(), + null, accessedTimestamp, contentId); + return updateCount.get(0) > 0; + } + + public static List findUsages(final String institutionUrl, final String itemUuid, final int itemVersion, + boolean versionIsLatest, boolean allVersions, boolean available) { + BbLogger.instance().logDebug("findUsages(" + institutionUrl + ", " + itemUuid + ", " + itemVersion + ", " + + allVersions + ", " + available + ")"); + ensureDatabase(); + + final StringBuilder sql = select().append("WHERE INSTURL=? AND UUID=?"); + if (!allVersions) { + sql.append(" AND (VERSION=?"); + if (versionIsLatest) { + sql.append(" OR VERSION=0"); + } + sql.append(")"); + } + if (available) { + sql.append(" AND AVAILABLE=? AND COURSEAVAILABLE=?"); + } + sql.append(" ORDER BY DTCREATED DESC"); + + final List usages = SqlUtil.runSql(sql.toString(), new ResultProcessor() { + @Override + public List getResults(ResultSet results) throws SQLException { + final List u = new ArrayList(); + while (results.next()) { + final String contentId = results.getString(COL_CONTENT_ID); + final ItemInfo r = new ItemInfo(institutionUrl, itemUuid, results.getInt(COL_VERSION), contentId, + results.getString(COL_COURSE_ID), results.getString(COL_FOLDER_ID), results.getString(COL_PAGE)); + r.setCreatedDate(results.getTimestamp(COL_DATE_CREATED)); + r.setModifiedDate(results.getTimestamp(COL_DATE_MODIFIED)); + r.setAvailable(results.getBoolean(COL_AVAILABLE)); + r.setName(results.getString(COL_BLACKBOARD_TITLE)); + r.setDescription(results.getString(COL_BLACKBOARD_DESC)); + r.getItemKey().setDatabaseId(results.getInt(COL_DATABASE_ID)); + r.setAttachmentName(results.getString(COL_ATTACHMENT_NAME)); + u.add(r); + } + return u; + } + }, institutionUrl, itemUuid, new OptionalParam(itemVersion, !allVersions), + new OptionalParam(available, available), new OptionalParam(available, available)); + return usages; + } + + public static List findAllUsages(final String institutionUrl, final String query, final String courseId, + String folderId, boolean available, String sortColumn, boolean sortReverse) { + BbLogger.instance().logDebug("findAllUsages(" + institutionUrl + ", " + query + ", " + courseId + ", " + folderId + + ", " + available + ", " + sortColumn + ", " + sortReverse + ")"); + ensureDatabase(); + + final StringBuilder sql = select().append(" WHERE INSTURL=?"); + + final boolean hasQuery = !Strings.isNullOrEmpty(query); + String likeQuery = null; + if (hasQuery) { + likeQuery = '%' + query.toLowerCase() + '%'; + sql.append(" AND LOWER(BBTITLE) LIKE ?"); + } + final boolean hasCourseId = !Strings.isNullOrEmpty(courseId); + if (hasCourseId) { + sql.append(" AND COURSEID = ?"); + } + final boolean hasFolderId = !Strings.isNullOrEmpty(folderId); + if (hasFolderId) { + sql.append(" AND FOLDERID = ?"); + } + if (available) { + sql.append(" AND AVAILABLE=? AND COURSEAVAILABLE=?"); + } + + String sortCol = "DTCREATED"; + String sortOrd = sortReverse ? "DESC" : "ASC"; + if (!Strings.isNullOrEmpty(sortColumn)) { + if (sortColumn.equals("name")) { + sortCol = "LOWER(BBTITLE)"; + } else if (sortColumn.equals("course")) { + sortCol = "LOWER(COURSENAME)"; + } + } + sql.append(" ORDER BY " + sortCol + " " + sortOrd); + + // @formatter:off + final List usages = SqlUtil.runSql(sql.toString(), new ResultProcessor() { + @Override + public List getResults(ResultSet results) throws SQLException { + final List u = new ArrayList(); + while (results.next()) { + final ItemInfo r = new ItemInfo(institutionUrl, results.getString(COL_UUID), results.getInt(COL_VERSION), + results.getString(COL_CONTENT_ID), results.getString(COL_COURSE_ID), results.getString(COL_FOLDER_ID), + results.getString(COL_PAGE)); + r.setCreatedDate(results.getTimestamp(COL_DATE_CREATED)); + r.setModifiedDate(results.getTimestamp(COL_DATE_MODIFIED)); + r.setAvailable(results.getBoolean(COL_AVAILABLE)); + r.setName(results.getString(COL_BLACKBOARD_TITLE)); + r.setDescription(results.getString(COL_BLACKBOARD_DESC)); + r.getItemKey().setDatabaseId(results.getInt(COL_DATABASE_ID)); + r.setDateAccessed(results.getTimestamp(COL_DATE_ACCESSED)); + r.setAttachmentName(results.getString(COL_ATTACHMENT_NAME)); + u.add(r); + } + return u; + } + }, institutionUrl, new OptionalParam(likeQuery, hasQuery), new OptionalParam(courseId, hasCourseId), + new OptionalParam(folderId, hasFolderId), new OptionalParam(available, available), + new OptionalParam(available, available)); + // @formatter:on + return usages; + } + + // Used by SynchroniseContentThread + public static List findEquellaContentByCourse(final String institutionUrl, final String courseId, + boolean available) { + BbLogger.instance() + .logDebug("findEquellaContentByCourse(" + institutionUrl + ", " + courseId + ", " + available + ")"); + ensureDatabase(); + + // ID, UUID, VERSION, FOLDERID, ATTACHMENT_NAME, CONTENTID + final StringBuilder sql = select(COL_DATABASE_ID, COL_UUID, COL_VERSION, COL_FOLDER_ID, COL_PAGE, COL_CONTENT_ID) + .append("WHERE INSTURL=? AND COURSEID=?"); + if (available) { + sql.append(" AND AVAILABLE=? AND COURSEAVAILABLE=?"); + } + + final List usages = SqlUtil.runSql(sql.toString(), new ResultProcessor() { + @Override + public List getResults(ResultSet results) throws SQLException { + List u = new ArrayList(); + while (results.next()) { + final int dbId = results.getInt(COL_DATABASE_ID); + final String uuid = results.getString(COL_UUID); + final int version = results.getInt(COL_VERSION); + final String folderId = results.getString(COL_FOLDER_ID); + final String page = results.getString(COL_PAGE); + final String contentId = results.getString(COL_CONTENT_ID); + + final ItemKey k = new ItemKey(institutionUrl, uuid, version, contentId, courseId, folderId, page); + k.setDatabaseId(dbId); + u.add(k); + } + return u; + } + }, institutionUrl, courseId, new OptionalParam(available, available), + new OptionalParam(available, available)); + return usages; + } + + public static int cleanupBadContent(final String institutionUrl) { + BbLogger.instance().logDebug("cleanupBadContent(" + institutionUrl + ")"); + ensureDatabase(); + + String sql = delete() + "WHERE INSTURL=? AND CONTENTID IS NULL"; + List updates = SqlUtil.runSql(sql, null, institutionUrl); + return updates.get(0); + } + + public static ContentRegisteredResponse contentIsRegistered(ItemKey itemKey) { + BbLogger.instance().logDebug("contentIsRegistered(" + itemKey + ")"); + ensureDatabase(); + + final ResultProcessor resultProcessor = new ResultProcessor() { + @Override + public List getResults(ResultSet results) throws SQLException { + List r = new ArrayList(); + while (results.next()) { + r.add(new Object[] { results.getInt(COL_DATABASE_ID), results.getBoolean(COL_AVAILABLE), + results.getString(COL_CONTENT_ID) }); + } + return r; + } + }; + + final List res; + final StringBuilder sql = select(COL_DATABASE_ID, COL_AVAILABLE, COL_CONTENT_ID); + if (itemKey.getContentId() != null) { + sql.append("WHERE CONTENTID=?"); + res = SqlUtil.runSql(sql.toString(), resultProcessor, itemKey.getContentId()); + } else { + // OLD STYLE + final String page = itemKey.getPage(); + sql.append("WHERE INSTURL=? AND UUID=? AND VERSION=? AND COURSEID=? AND FOLDERID=? AND PAGE" + + (Strings.isNullOrEmpty(page) ? " IS NULL" : "=?")); + + res = SqlUtil.runSql(sql.toString(), resultProcessor, itemKey.getInstitutionUrl(), itemKey.getUuid(), + itemKey.getVersion(), itemKey.getCourseId(), itemKey.getFolderId(), + new OptionalParam(page, !Strings.isNullOrEmpty(page))); + } + + final ContentRegisteredResponse response = new ContentRegisteredResponse(); + if (!res.isEmpty()) { + response.setRegistered(true); + final Object[] r = res.get(0); + response.setId((Integer) r[0]); + response.setAvailable((Boolean) r[1]); + response.setContentId((String) r[2]); + } + + return response; + } + + private static void ensureDatabase() { + if (databaseVersion < 1) { + try { + SqlUtil.runSql(SqlUtil.select("1").toString(), null); + databaseVersion = 1; + } catch (Exception e) { + makeDatabase(); + databaseVersion = 1; + } + } + + if (databaseVersion < 2) { + if (!SqlUtil.columnExists(COL_CONTENT_ID)) { + modifyDatabaseAddContentAndCourseName(); + } + databaseVersion = 2; + } + + if (databaseVersion < 3) { + if (!SqlUtil.columnExists(COL_BLACKBOARD_DESC)) { + modifyDatabaseAddDescription(); + } + databaseVersion = 3; + } + + if (databaseVersion < 4) { + if (!SqlUtil.columnExists(COL_DATE_ACCESSED)) { + modifyDatabaseAddDateAccessed(); + } + databaseVersion = 4; + } + + if (databaseVersion < 5) { + if (!SqlUtil.columnExists(COL_ATTACHMENT_NAME)) { + modifyDatabaseAddAttachmentName(); + } + databaseVersion = 5; + } + } + + /** + * Todo: this clearly isn't normalised. But the question is, is it worth it? + */ + private static void makeDatabase() { + final String sql; + if (getDatabase().isOracle()) { + BbLogger.instance().logDebug("Oracle DB detected"); + + SqlUtil.runSql("CREATE SEQUENCE equellaseq START WITH 1 INCREMENT BY 1 NOMAXVALUE", null); + + // @formatter:off + sql = "CREATE TABLE equellacontent (" + "ID NUMBER NOT NULL, " + "INSTURL NVARCHAR2(255) NOT NULL, " + + "DTCREATED DATE DEFAULT SYSDATE, " + "DTMODIFIED DATE DEFAULT SYSDATE, " + + "BBTITLE NVARCHAR2(1024) NOT NULL," + "PAGE NVARCHAR2(1024)," // nullable for item summaries + + "UUID NVARCHAR2(40) NOT NULL, " + "VERSION NUMBER NOT NULL," + "COURSEID NVARCHAR2(32) NOT NULL, " + + "FOLDERID NVARCHAR2(32) NOT NULL, " + "AVAILABLE NUMBER NOT NULL," + "COURSEAVAILABLE NUMBER NOT NULL," + + "CONSTRAINT equellacontent_pk PRIMARY KEY (ID))"; + // @formatter:on + SqlUtil.runSql(sql, null); + + SqlUtil.runSql("CREATE INDEX eqcontent_uuidversion ON equellacontent(INSTURL, UUID, VERSION)", null); + // cannot be UNIQUE since same item could be added to same folder + // (in theory) + SqlUtil.runSql("CREATE INDEX eqcontent_all ON equellacontent(INSTURL, UUID, VERSION, COURSEID, FOLDERID)", null); + SqlUtil.runSql("CREATE INDEX eqcontent_course ON equellacontent(INSTURL, COURSEID)", null); + SqlUtil.runSql("CREATE INDEX eqcontent_available ON equellacontent(AVAILABLE, COURSEAVAILABLE)", null); + } else { + BbLogger.instance().logDebug("SQL Server DB detected"); + // @formatter:off + sql = "CREATE TABLE dbo.equellacontent (" + "ID int NOT NULL PRIMARY KEY IDENTITY, " + + "INSTURL nvarchar(255) NOT NULL, " + "BBTITLE nvarchar(1024) NOT NULL, " + "PAGE nvarchar(1024) NULL, " // nullable + // for + // item + // summaries + + "DTCREATED datetime NOT NULL, " + "DTMODIFIED datetime NOT NULL, " + "UUID nvarchar(40) NOT NULL, " + + "VERSION int NOT NULL," + "COURSEID nvarchar(32) NOT NULL, " + "FOLDERID nvarchar(32) NOT NULL, " + + "AVAILABLE bit NOT NULL," + "COURSEAVAILABLE bit NOT NULL" + ")"; + // @formatter:on + SqlUtil.runSql(sql, null); + + SqlUtil.runSql("CREATE INDEX eqcontent_course ON dbo.equellacontent(INSTURL, COURSEID)", null); + // cannot be UNIQUE since same item could be added to same folder + // (in theory) + SqlUtil.runSql("CREATE INDEX eqcontent_all ON dbo.equellacontent(INSTURL, UUID, VERSION, COURSEID, FOLDERID)", + null); + SqlUtil.runSql("CREATE INDEX eqcontent_uuidversion ON dbo.equellacontent(INSTURL, UUID, VERSION)", null); + SqlUtil.runSql("CREATE INDEX eqcontent_available ON dbo.equellacontent(AVAILABLE, COURSEAVAILABLE)", null); + } + } + + private static void modifyDatabaseAddContentAndCourseName() { + if (getDatabase().isOracle()) { + // debug("Oracle DB detected"); + // @formatter:off + final String sql = "ALTER TABLE equellacontent ADD (" + "CONTENTID NVARCHAR2(32) NULL, " + + "COURSENAME NVARCHAR2(512) NULL" + ")"; + // @formatter:on + SqlUtil.runSql(sql, null); + SqlUtil.runSql("CREATE INDEX eqcontent_content ON equellacontent(INSTURL, CONTENTID)", null); + } else { + // debug("SQL Server DB detected"); + // @formatter:off + final String sql = "ALTER TABLE dbo.equellacontent ADD " + "CONTENTID nvarchar(32) NULL, " + + "COURSENAME nvarchar(512) NULL"; + // @formatter:on + SqlUtil.runSql(sql, null); + SqlUtil.runSql("CREATE INDEX eqcontent_content ON dbo.equellacontent(INSTURL, CONTENTID)", null); + } + } + + private static void modifyDatabaseAddDescription() { + final String sql; + if (getDatabase().isOracle()) { + sql = "ALTER TABLE equellacontent ADD (BBDESC NVARCHAR2(512) NULL)"; + } else { + sql = "ALTER TABLE dbo.equellacontent ADD BBDESC nvarchar(512) NULL"; + } + SqlUtil.runSql(sql, null); + } + + private static void modifyDatabaseAddDateAccessed() { + final String sql; + if (getDatabase().isOracle()) { + sql = "ALTER TABLE equellacontent ADD (" + COL_DATE_ACCESSED + " DATE NULL)"; + } else { + sql = "ALTER TABLE dbo.equellacontent ADD " + COL_DATE_ACCESSED + " datetime NULL"; + } + SqlUtil.runSql(sql, null); + } + + private static void modifyDatabaseAddAttachmentName() { + final String sql; + if (getDatabase().isOracle()) { + sql = "ALTER TABLE equellacontent ADD (" + COL_ATTACHMENT_NAME + " NVARCHAR2(512) NULL)"; + } else { + sql = "ALTER TABLE dbo.equellacontent ADD " + COL_ATTACHMENT_NAME + " nvarchar(512) NULL"; + } + SqlUtil.runSql(sql, null); + } + + private static BbDatabase getDatabase() { + final DatabaseContainer dbContainer = (DatabaseContainer) PersistenceServiceFactory.getInstance() + .getDbPersistenceManager().getContainer(); + return dbContainer.getBbDatabase(); + } + + private static String trim(String text, int maxlen) { + if (text != null && text.length() > maxlen) { + return text.substring(0, maxlen); + } + return text; + } +} diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/BadCharacterFilterReader.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/BadCharacterFilterReader.java new file mode 100644 index 0000000..fce0ace --- /dev/null +++ b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/BadCharacterFilterReader.java @@ -0,0 +1,237 @@ +package org.apereo.openequella.integration.blackboard.common.propbag; + +import java.io.FilterReader; +import java.io.IOException; +import java.io.Reader; + +/** + * keeps reading until we get a '&' and then buffer the rest as 'suspect' + * until a ';' is read. Only then can we determine if the suspect buffer is + * illegal and can be discarded, OR it's okay and we can 'read' it in. If we + * read in an illegal unicode char, it can be discarded straight away. This code + * is deliberately mad-hacks (eg unrolled loops and inline method code) for + * performance reasons + * + * @author aholland + */ +public class BadCharacterFilterReader extends FilterReader { + private static final int DEFAULT_CHUNK = 1000; + private static final int MAX_SUSPECTION_LENGTH = 10; + + protected final char[] chunkBuff; + protected final char[] suspect = new char[MAX_SUSPECTION_LENGTH]; + + protected int suspectStart; + protected int suspectLength; + + protected boolean didDiscard; + + public BadCharacterFilterReader(final Reader reader) { + this(reader, DEFAULT_CHUNK); + } + + public BadCharacterFilterReader(final Reader reader, final int bufferSize) { + super(reader); + if (bufferSize <= MAX_SUSPECTION_LENGTH) { + throw new IllegalArgumentException("bufferSize must be at least " + MAX_SUSPECTION_LENGTH); //$NON-NLS-1$ + } + chunkBuff = new char[bufferSize]; + + // I assume the super (Reader) constructor initialises its lock Object? + // But just in case, affirm it here. + if (lock == null) { + lock = new Object(); + } + } + + @Override + public int read() { + throw new UnsupportedOperationException(); + } + + @Override + public int read(final char[] outBuffer, final int originalWriteOffset, final int len) throws IOException { + synchronized (lock) { + if (in == null) { + throw new IOException("Stream closed"); //$NON-NLS-1$ + } + + int writeOffset = originalWriteOffset; + final int maxWriteSize = (chunkBuff.length < len ? chunkBuff.length : len); + + // always flush the suspect buffer it needs to be. + // suspectStart > 0 means we have already started writing it + if (suspectStart > 0) { + final int slen = (suspectLength < maxWriteSize ? suspectLength : maxWriteSize); + return writeSuspect(outBuffer, originalWriteOffset, slen); + } else { + // note that we may be writing out a suspect buffer filled from + // a previous call + // so we cannot always read in the full allowed amount + final int maxWrappedReadSize = (maxWriteSize > suspectLength ? maxWriteSize - suspectLength : 0); + final int wrappedReadSize = in.read(chunkBuff, 0, maxWrappedReadSize); + + // not end of stream? + if (wrappedReadSize >= 0) { + for (int i = 0; i < wrappedReadSize; ++i) { + final int next = chunkBuff[i]; + + // invalid unicode char + if ((next < 0x20 && next != 0x9 && next != 0xA && next != 0xD) || (next > 0xD7FF && next < 0xE000) + || (next > 0xFFFD && next < 0x10000) || next > 0x10FFFF) { + // discard + didDiscard = true; + } else { + // are we suspicious? + if (suspectLength > 0) { + // is it a ';'? + if (next == ';') { + suspect[suspectStart + suspectLength] = (char) next; + ++suspectLength; + if (suspectLength == MAX_SUSPECTION_LENGTH) { + // it clearly isn't a suspect buffer + writeOffset += writeSuspect(outBuffer, writeOffset, len - writeOffset); + } else { + // so, is it dodgy? buffer at this point + // could be #x999, #99, amp or anything! + if (isDodgy()) { + // it IS dodgy. clear out suspect + // buffer + suspectStart = 0; + suspectLength = 0; + didDiscard = true; + } else { + writeOffset += writeSuspect(outBuffer, writeOffset, len - writeOffset); + } + } + } else { + suspect[suspectStart + suspectLength] = (char) next; + ++suspectLength; + if (suspectStart + suspectLength == MAX_SUSPECTION_LENGTH) { + // it clearly isn't a suspect buffer + writeOffset += writeSuspect(outBuffer, writeOffset, len - writeOffset); + } + } + } else { + // seems ok, is it a '&'? + if (next == '&') { + // start pumping the suspect buffer + suspect[suspectStart + suspectLength] = '&'; + ++suspectLength; + } else { + outBuffer[writeOffset] = (char) next; + ++writeOffset; + } + } + } + } + + return writeOffset - originalWriteOffset; + } + + // nothing left to read, do we have a suspect buffer to clear? + else { + if (suspectLength > 0) { + final int slen = (suspectLength < maxWriteSize ? suspectLength : maxWriteSize); + return writeSuspect(outBuffer, originalWriteOffset, slen); + } + + // this is it. it's all over + return -1; + } + } + } + } + + public boolean didDiscard() { + return didDiscard; + } + + @SuppressWarnings("nls") + protected int writeSuspect(final char[] output, final int position, final int maxLength) { + final int len = (suspectLength < maxLength ? suspectLength : maxLength); + if (suspect.length < len + suspectStart || output.length < len + position || len < 0) { + throw new ArrayIndexOutOfBoundsException( + "suspect buffer size: " + suspect.length + ", output buffer size: " + output.length + ", suspect start: " + + suspectStart + ", position: " + position + ", maxLength: " + maxLength); + } + System.arraycopy(suspect, suspectStart, output, position, len); + + // fully written buffer + if ((len == suspectLength)) { + suspectStart = 0; + suspectLength = 0; + } else { + suspectStart += len; + suspectLength -= len; + } + + return len; + } + + /** + * A very apt name for this code methinks... Preconditions: suspectStart is + * ALWAYS zero here suspect[0] == '&' ALWAYS suspect[suspectLength-1] == ';' + * ALWAYS + * + * @param suspect + * @param suspectLength + * @return + */ + protected boolean isDodgy() { + // if it starts with hash -> then it's a numbered escape sequence + if (suspect[1] == '#') { + if (suspectLength > 2) { + // is the next an x or a number? + final char c3 = suspect[2]; + if (c3 == 'x') { + if (suspectLength > 3) { + // at this point, bugger optimisations it's looking a + // fair chance + // bear in mind the suspect will always end in ';' at + // this point, hence the -4 + final String str = new String(suspect, 3, suspectLength - 4); + // is this a hex representation of an invalid range? + try { + final int i = Integer.parseInt(str, 16); + if ((i < 0x20 && i != 0x9 && i != 0xA && i != 0xD) || (i > 0xD7FF && i < 0xE000) + || (i > 0xFFFD && i < 0x10000) || i > 0x10FFFF) { + return true; + } + } catch (final NumberFormatException n) { + // nope + } + } + } else if (c3 == '0' || c3 == '1' || c3 == '2' || c3 == '3' || c3 == '4' || c3 == '5' || c3 == '6' || c3 == '7' + || c3 == '8' || c3 == '9') { + // at this point, bugger optimisations it's looking a fair + // chance + // bear in mind the suspect will always end in ';' at this + // point, hence the -3 + final String str = new String(suspect, 2, suspectLength - 3); + // is this a decimal representation of an invalid range? + try { + final int i = Integer.parseInt(str, 10); + if ((i < 0x20 && i != 0x9 && i != 0xA && i != 0xD) || (i > 0xD7FF && i < 0xE000) + || (i > 0xFFFD && i < 0x10000) || i > 0x10FFFF) { + return true; + } + } catch (final NumberFormatException n) { + // nope + } + } + } + } + return false; + } + + @Override + public void close() throws IOException { + synchronized (lock) { + if (in != null) { + in.close(); + in = null; + } + } + } +} diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/BadCharacterFilterWriter.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/BadCharacterFilterWriter.java new file mode 100644 index 0000000..e5f955e --- /dev/null +++ b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/BadCharacterFilterWriter.java @@ -0,0 +1,64 @@ +package org.apereo.openequella.integration.blackboard.common.propbag; + +import java.io.FilterWriter; +import java.io.IOException; +import java.io.Writer; + +/** + * Can only strip out invalid unicode chars as we see them... this means that + * escaped invalid chars will slip through the cracks. Eg &#x0B; Not as big + * a deal as it seems, when WE read the chars back in, they will be removed. If + * anyone else reads them in... unrucky. + * + * @author aholland + */ +public class BadCharacterFilterWriter extends FilterWriter { + protected boolean didDiscard; + private final Writer wrapped; + + public BadCharacterFilterWriter(final Writer writer) { + super(writer); + wrapped = writer; + } + + @Override + public void write(final String str) throws IOException { + write(str.toCharArray()); + } + + @Override + public void write(final int c) throws IOException { + // invalid unicode char + if ((c < 0x20 && c != 0x9 && c != 0xA && c != 0xD) || (c > 0xD7FF && c < 0xE000) || (c > 0xFFFD && c < 0x10000) + || c > 0x10FFFF) { + // discard + didDiscard = true; + } else { + wrapped.write(c); + } + } + + @Override + public void write(final char[] cbuf, final int off, final int len) throws IOException { + int writeLength = 0; + final char[] buff = new char[len]; + for (int i = off; i < off + len; ++i) { + final char c = cbuf[i]; + // invalid unicode char + if ((c < 0x20 && c != 0x9 && c != 0xA && c != 0xD) || (c > 0xD7FF && c < 0xE000) || (c > 0xFFFD && c < 0x10000) + || c > 0x10FFFF) { + // discard + didDiscard = true; + } else { + buff[writeLength++] = c; + } + } + if (writeLength > 0) { + wrapped.write(buff, 0, writeLength); + } + } + + public boolean didDiscard() { + return didDiscard; + } +} diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/DOMHelper.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/DOMHelper.java new file mode 100644 index 0000000..b62d404 --- /dev/null +++ b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/DOMHelper.java @@ -0,0 +1,168 @@ +/* + * Created on Dec 20, 2004 + */ +package org.apereo.openequella.integration.blackboard.common.propbag; + +import java.util.ArrayList; +import java.util.List; + +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +/** + * @author Nicholas Read + */ +final class DOMHelper { + private DOMHelper() { + throw new IllegalAccessError("Do not invoke"); //$NON-NLS-1$ + } + + /** + * Removes namespace from a given path element. + */ + static String stripNamespace(String name) { + int index = name.indexOf(':'); + if (index >= 0) { + name = name.substring(index + 1); + } + return name; + } + + /** + * Removes namespace from a given path element. + */ + static String stripAttribute(String name) { + if (name.startsWith("@")) //$NON-NLS-1$ + { + return name.substring(1); + } else { + return name; + } + } + + /** + * Retrieves the text value from a node's child text node. + */ + static String getValueForNode(Node node, String defaultValue) { + String value = null; + if (node != null) { + switch (node.getNodeType()) { + case Node.ELEMENT_NODE: { + Node textNode = node.getFirstChild(); + if (textNode != null) { + value = textNode.getNodeValue(); + } + break; + } + + case Node.ATTRIBUTE_NODE: { + value = node.getNodeValue(); + break; + } + + default: + break; + } + } + + if (value == null) { + return defaultValue; + } else { + return value; + } + } + + /** + * Retrieves the text value from a node's child text node. + */ + static void setValueForNode(Node node, String value) { + if (node != null) { + switch (node.getNodeType()) { + case Node.ELEMENT_NODE: { + Node child = node.getFirstChild(); + if (child == null) { + Document doc = node.getOwnerDocument(); + node.appendChild(doc.createTextNode(value)); + } else { + child.setNodeValue(value); + } + break; + } + + case Node.ATTRIBUTE_NODE: { + + Attr attribute = (Attr) node; + attribute.getOwnerElement().setAttribute(attribute.getName(), value); + break; + } + + default: + break; + } + } + } + + static Node findNext(Node child, String nodeName) { + for (; child != null; child = child.getNextSibling()) { + if (child.getNodeType() == Node.ELEMENT_NODE) { + if (nodeName == null || nodeName.equals("*") //$NON-NLS-1$ + || nodeName.equals(DOMHelper.stripNamespace(child.getNodeName()))) { + return child; + } + } + } + return null; + } + + /** + * @return a vector containing all the delimited String sections + */ + static List splitPath(String path) { + List parts = new ArrayList(); + + String[] split = path.split("/"); //$NON-NLS-1$ + for (int i = 0; i < split.length; i++) { + if (split[i].length() > 0) { + boolean isLastPart = i == split.length - 1; + if (!isLastPart && split[i].indexOf('@') >= 0) { + throw new IllegalArgumentException("Attribute must be last component of path"); //$NON-NLS-1$ + } + parts.add(split[i]); + } + } + + return parts; + } + + static boolean hasChildElement(Node node) { + for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) { + if (child.getNodeType() == Node.ELEMENT_NODE) { + return true; + } + } + return false; + } + + static boolean removeNode(Node node) { + if (node != null) { + switch (node.getNodeType()) { + case Node.ELEMENT_NODE: { + Node parent = node.getParentNode(); + if (parent != null) { + parent.removeChild(node); + return true; + } + break; + } + + case Node.ATTRIBUTE_NODE: { + Attr attr = (Attr) node; + attr.getOwnerElement().removeAttribute(attr.getName()); + return true; + } + } + } + return false; + } +} \ No newline at end of file diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/PropBagMin.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/PropBagMin.java new file mode 100644 index 0000000..a4ab834 --- /dev/null +++ b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/PropBagMin.java @@ -0,0 +1,845 @@ +package org.apereo.openequella.integration.blackboard.common.propbag; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.Writer; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.DocumentType; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; + +import com.google.common.base.Throwables; +import com.google.common.collect.Lists; +import com.google.common.io.Closeables; + +/** + * A subset of the actual PropBagEx class. Blackboard makes me cry, they still + * haven't fixed the upload webservice problem. + */ +@SuppressWarnings("nls") +public class PropBagMin { + private static final String WILD = "*"; + private static final String PATH_SEP = "/"; + private static final String BLANK = ""; + private static final String ATTR = "@"; + + private static DocumentBuilderFactory factory; + private Element m_elRoot; + + static { + if (factory == null) { + factory = DocumentBuilderFactory.newInstance(); + factory.setValidating(false); + + try { + factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + } catch (ParserConfigurationException e) { + // nothing + } catch (NoSuchMethodError nup) { + // java 1.4,doesn't like it + } + factory.setNamespaceAware(false); + } + } + + /** + * Creates a new PropBagEx rooted at the given node. + * + * @param node + * The node to root the new PropBagEx at. + * @param sameDocument + * Use the nodes document for the new PropBagEx. + */ + private PropBagMin(final Node n, final boolean sameDocument) { + if (sameDocument) { + m_elRoot = (Element) n; + } else { + Document doc; + try { + DocumentBuilder builder; + synchronized (factory) { + builder = factory.newDocumentBuilder(); + } + doc = builder.getDOMImplementation().createDocument(null, null, null); + + final Node imp = importNode(doc, n, true); + doc.appendChild(imp); + + m_elRoot = (Element) imp; + } catch (final ParserConfigurationException pce) { + throw propagate(pce); + } + } + } + + /** + * Creates a new PropBagEx from the given string. + * + * @param szXML + * The string to read. + * @throws PropBagExException + * if the XML being read is invalid. + */ + public PropBagMin(final String szXML) { + setXML(new StringReader(szXML)); + } + + /** + * Creates a new PropBagEx from the given stream. + * + * @param is + * The stream to read from. + * @throws PropBagExException + * if the XML being read is invalid. + */ + public PropBagMin(final InputStream is) { + setXML(new UnicodeReader(is, "UTF-8")); + } + + /** + * A helper method that should be called by each method before using + * #getNodeHelper(..) or such. + */ + private void ensureRoot() { + if (m_elRoot == null) { + clear(); + } + } + + @SuppressWarnings("null") + private Node lookupNode(final Node parent, String nodeName, final int index, final boolean create) { + Node foundNode = null; + + int nNumFound = 0; + final Document doc = parent.getOwnerDocument(); + + final boolean isAttribute = nodeName.startsWith(ATTR); + nodeName = DOMHelper.stripAttribute(nodeName); + if (isAttribute) { + foundNode = ((Element) parent).getAttributeNode(nodeName); + } else { + nodeName = DOMHelper.stripNamespace(nodeName); + final boolean matchAny = nodeName.equals(WILD); + + final NodeList children = parent.getChildNodes(); + for (int i = 0; i < children.getLength() && foundNode == null; i++) { + final Node child = children.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + final String childName = DOMHelper.stripNamespace(child.getNodeName()); + if (matchAny || nodeName.equals(childName)) { + if (nNumFound != index) { + nNumFound++; + } else { + foundNode = child; + break; + } + } + } + } + } + + if (foundNode == null && create == true) { + // If the Index is 0 and we didn't find a node or if the number + // found (which is not zero based) equals the index (which is) + // then this is the same as saying index is one more that the + // number of nodes that exist then add a new child node. + if (index == 0 || nNumFound == index) { + if (isAttribute) { + ((Element) parent).setAttribute(nodeName, BLANK); + foundNode = ((Element) parent).getAttributeNode(nodeName); + } else { + foundNode = doc.createElement(nodeName); + parent.appendChild(foundNode); + } + } else { + throw new IndexOutOfBoundsException(); + } + } + return foundNode; + } + + private int getIndexValue(final String[] aszNodeName) { + aszNodeName[1] = aszNodeName[0]; + + final int index = aszNodeName[0].indexOf('['); + if (index >= 0) { + aszNodeName[1] = aszNodeName[0].substring(0, index); + + // Sanity Check + if (index + 1 < aszNodeName[0].length()) { + final String szIndex = aszNodeName[0].substring(index + 1, aszNodeName[0].length() - 1); + return Integer.parseInt(szIndex); + } + } + + // Index is the first instance. + return 0; + } + + /** + * Gets the node for the given path. + */ + private Node getNodeHelper(final String path, final boolean bCreate, final boolean bNew) { + // Split on the path identifier + final List pathComponents = DOMHelper.splitPath(path); + + Node node = m_elRoot; + final Document doc = node.getOwnerDocument(); + + final Iterator iter = pathComponents.iterator(); + while (iter.hasNext() && node != null) { + final String[] aszNodeName = new String[2]; + aszNodeName[0] = iter.next(); + + // Extract the index if that exists + final int nIndex = getIndexValue(aszNodeName); + if (bNew && !iter.hasNext()) { + final Node child = doc.createElement(aszNodeName[1]); + node.appendChild(child); + return child; + } else { + node = lookupNode(node, aszNodeName[1], nIndex, bCreate); + } + } + + return node; + } + + private Writer genXML(final Writer sbuf, final Node subRoot) { + try { + boolean bEndElem = false; + final int type = subRoot.getNodeType(); + switch (type) { + case Node.DOCUMENT_TYPE_NODE: { + final DocumentType doctype = (DocumentType) subRoot; + sbuf.write("\n"); + // doc.getDoctype(); + // System.out.println(""); + break; + } + case Node.ELEMENT_NODE: { + sbuf.write('<'); + sbuf.write(subRoot.getNodeName()); + final NamedNodeMap nnm = subRoot.getAttributes(); + if (nnm != null) { + final int len = nnm.getLength(); + Attr attr; + for (int i = 0; i < len; i++) { + attr = (Attr) nnm.item(i); + sbuf.write(' ' + attr.getNodeName() + "=\"" + ent(attr.getNodeValue()) + '"'); + } + } + // Check for an empty parent element + // no children, or a single TEXT_NODE with length() == 0 + final Node child = subRoot.getFirstChild(); + if (child == null || (child.getNodeType() == Node.TEXT_NODE + && (child.getNextSibling() == null && child.getNodeValue().length() == 0))) { + sbuf.write(PATH_SEP); + } else { + bEndElem = true; + } + sbuf.write('>'); + break; + } + case Node.ENTITY_REFERENCE_NODE: { + + sbuf.write('&' + subRoot.getNodeName() + ';'); + break; + } + case Node.CDATA_SECTION_NODE: { + sbuf.write(""); + break; + } + case Node.TEXT_NODE: { + sbuf.write(ent(subRoot.getNodeValue())); + break; + } + case Node.PROCESSING_INSTRUCTION_NODE: { + sbuf.write(" 0) { + sbuf.write(' '); + sbuf.write(data); + } + sbuf.write("?>"); + break; + } + case Node.COMMENT_NODE: { + sbuf.write(""); + break; + } + } + + for (Node child = subRoot.getFirstChild(); child != null; child = child.getNextSibling()) { + genXML(sbuf, child); + } + + if (bEndElem) { + sbuf.write(""); + } + } catch (final IOException e) { + throw propagate(e); + } + + return sbuf; + } + + private String ent(final String text) { + final StringBuilder szOut = new StringBuilder(); + final char[] chars = text.toCharArray(); + for (final char ch : chars) { + switch (ch) { + case '<': + szOut.append("<"); + break; + + case '>': + szOut.append(">"); + break; + + case '&': + szOut.append("&"); + break; + + case '"': + szOut.append("""); + break; + + default: + // http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char + // regular displayable ASCII: + if (ch == 0xA || ch == 0xD || ch == 0x9 || (ch >= 0x20 && ch <= 0x007F)) { + szOut.append(ch); + } else if ((ch > 0x007F && ch <= 0xD7FF) || (ch >= 0xE000 && ch <= 0xFFFD) + || (ch >= 0x10000 && ch <= 0x10FFFF)) { + szOut.append("&#x"); + szOut.append(hex(ch)); + szOut.append(';'); + } + // else we discard the character entirely. + // It CANNOT be placed in XML + break; + } + } + return szOut.toString(); + } + + private String hex(final char c) { + final String hexed = Integer.toHexString(c); + final int deficit = 4 - hexed.length(); + // wooo, unrolled loops + switch (deficit) { + case 1: + return "0" + hexed; + case 2: + return "00" + hexed; + case 3: + return "000" + hexed; + default: + return hexed; + } + } + + private Node getRootDoc() { + if (m_elRoot.getOwnerDocument().getDocumentElement() == m_elRoot) { + return m_elRoot.getOwnerDocument(); + } else { + return m_elRoot; + } + } + + @Override + public String toString() { + ensureRoot(); + // genXML returns the same StringBuffer that we pass in. + final StringWriter stringWriter = new StringWriter(); + genXML(new BadCharacterFilterWriter(stringWriter), getRootDoc()); + return stringWriter.toString(); + } + + /** + * Iterates over all nodes matching the given parent and name. + */ + private abstract static class InternalIterator implements Iterator, Iterable { + protected Node parent; + protected String name; + protected Node upto; + protected Node last; + protected Node root; + + public InternalIterator(final Node parent, final Node first, final String name, final Node root) { + this.parent = parent; + this.name = name; + this.root = root; + upto = first; + if (parent == null) { + upto = null; + } else if (upto == null) { + upto = DOMHelper.findNext(parent.getFirstChild(), name); + } + } + + protected void moveOn() { + last = upto; + if (upto == root) { + upto = null; + } else { + upto = DOMHelper.findNext(last.getNextSibling(), name); + } + } + + /* + * (non-Javadoc) + * + * @see java.lang.Iterable#iterator() + */ + @Override + public Iterator iterator() { + return this; + } + + /* + * (non-Javadoc) + * + * @see java.util.Iterator#remove() + */ + @Override + public void remove() { + if (last == null) { + throw new IllegalStateException(); + } + parent.removeChild(last); + } + + /* + * (non-Javadoc) + * + * @see java.util.Iterator#hasNext() + */ + @Override + public boolean hasNext() { + return upto != null; + } + + /* + * (non-Javadoc) + * + * @see java.util.Iterator#next() + */ + @Override + public T next() { + moveOn(); + return getNextValue(); + } + + protected abstract T getNextValue(); + } + + /** + * Iterates over all nodes matching the given parent and name. The iteration + * will return PropBagEx's rooted at each node. + */ + public static class PropBagIterator extends InternalIterator { + public PropBagIterator(final Node parent, final Node first, final String name, final Node root) { + super(parent, first, name, root); + } + + @Override + protected PropBagMin getNextValue() { + return new PropBagMin(last, true); + } + } + + /** + * Iterates over all nodes from the parent, that match the given path at any of + * it's components. + */ + private abstract static class ListOfNodesIterator implements Iterator, Iterable { + protected final LinkedList nodes = new LinkedList(); + + protected Node last; + + protected void moveOn() { + last = nodes.removeFirst(); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Iterable#iterator() + */ + @Override + public Iterator iterator() { + return this; + } + + /* + * (non-Javadoc) + * + * @see java.util.Iterator#remove() + */ + @Override + public void remove() { + if (last == null) { + throw new IllegalStateException(); + } + last.getParentNode().removeChild(last); + } + + /* + * (non-Javadoc) + * + * @see java.util.Iterator#hasNext() + */ + @Override + public boolean hasNext() { + return !nodes.isEmpty(); + } + + /* + * (non-Javadoc) + * + * @see java.util.Iterator#next() + */ + @Override + public T next() { + moveOn(); + return getNextValue(); + } + + protected abstract T getNextValue(); + } + + /** + * Iterates over all nodes from the parent, that match the given path at any of + * it's components. + */ + private abstract static class InternalThoroughIterator extends ListOfNodesIterator { + public InternalThoroughIterator(final Node parent, final String path) { + final List names = Lists.newArrayList(path.split(PATH_SEP)); + for (final Iterator iter = names.iterator(); iter.hasNext();) { + final String p = iter.next(); + if (p.trim().length() == 0) { + iter.remove(); + } + } + + if (!names.isEmpty()) { + findAllNodes(parent, names, 0); + } + } + + protected void findAllNodes(final Node parent, final List names, final int index) { + final String path = names.get(index); + if (!path.startsWith(ATTR)) { + Node find = DOMHelper.findNext(parent.getFirstChild(), path); + while (find != null) { + if (index == names.size() - 1) { + nodes.add(find); + } else { + findAllNodes(find, names, index + 1); + } + find = DOMHelper.findNext(find.getNextSibling(), path); + } + } else { + if (index == names.size() - 1) { + final Node attr = parent.getAttributes().getNamedItem(path.substring(1)); + nodes.add(attr); + } else { + throw new RuntimeException("Xpath has an attribute component before the last component"); + } + } + } + } + + /** + * Iterates over all nodes from the parent, that match the given path at any of + * it's components. The iteration will return PropBag's rooted at each of each + * nodes. + */ + public static class PropBagMinThoroughIterator extends InternalThoroughIterator { + public PropBagMinThoroughIterator(final Node parent, final String name) { + super(parent, name); + } + + @Override + protected PropBagMin getNextValue() { + return new PropBagMin(last, true); + } + } + + /** + * Iterates over all nodes from the parent, that match the given path at any of + * it's components. The iteration will return each Node. + */ + public static class NodeThoroughIterator extends InternalThoroughIterator { + public NodeThoroughIterator(final Node parent, final String name) { + super(parent, name); + } + + @Override + protected Node getNextValue() { + return last; + } + } + + /** + * Creates an iterator which return's PropBagEx's for all children of the root + * element + */ + public PropBagIterator iterator() { + ensureRoot(); + return new PropBagIterator(m_elRoot, null, null, m_elRoot); + } + + /** + * Creates an iterator which return's PropBagEx's for each child for the path + * given
+ * E.g.
+ * /path/node/item
+ * Will iterate over all the "item" nodes. + * + * @param path + * @return + */ + public PropBagIterator iterator(final String path) { + ensureRoot(); + + String name = null; + Node parent = null; + + final Node node = getNodeHelper(path, false, false); + if (node != null) { + parent = node.getParentNode(); + + // see Jira Defect TLE-1293 : + // http://apps.dytech.com.au/jira/browse/TLE-1293 + name = DOMHelper.stripNamespace(node.getNodeName()); + if (path.endsWith(WILD)) { + name = WILD; + } + + } + return new PropBagIterator(parent, node, name, m_elRoot); + } + + /** + * Iterates over all nodes that match the given path, like an XSLT does. + */ + public PropBagMinThoroughIterator iterateAll(final String path) { + ensureRoot(); + return new PropBagMinThoroughIterator(m_elRoot, path); + } + + private void setXML(final Reader reader) { + BufferedReader filterer = null; + try { + filterer = new BufferedReader(new BadCharacterFilterReader(reader)); + DocumentBuilder builder; + + synchronized (factory) { + builder = factory.newDocumentBuilder(); + } + Document doc = builder.parse(new InputSource(filterer)); + + // Get the root element + m_elRoot = doc.getDocumentElement(); + } catch (Exception ex) { + throw propagate(ex); + } finally { + if (filterer != null) { + try { + Closeables.close(filterer, false); + } catch (IOException e) { + // Whatever + } + } + } + } + + /** + * retrieves the text delimited by the given node's tags + * + * @param path + * full name of the node, parents qualified by '/' + * @return String content of element + */ + public String getNode(final String path) { + return getNode(path, BLANK); + } + + /** + * retrieves the text delimited by the given node's tags + * + * @param path + * full name of the node, parents qualified by '/' + * @return String content of element + */ + public String getNode(final String path, final String defaultValue) { + ensureRoot(); + final Node oNode = getNodeHelper(path, false, false); + return DOMHelper.getValueForNode(oNode, defaultValue); + } + + /** + * Retrieves a node as an int value given it's name. If the node does not exist + * or its value is an invalid integer, then the default value is returned. + * + * @param szFullNodeName + * full name of the node, parents qualified by '/' + * @param defaultValue + * A default value to return if the node does not exist. + * @return value of the node. + */ + public int getIntNode(final String path, final int defaultValue) { + ensureRoot(); + try { + return Integer.parseInt(getNode(path)); + } catch (final NumberFormatException ex) { + return defaultValue; + } + } + + public void setNode(final String path, final String value) { + ensureRoot(); + final Node node = getNodeHelper(path, true, false); + DOMHelper.setValueForNode(node, value); + } + + /** + * Checks to see if a node exists + * + * @param path + * The path to the node. + * @return true if one or more nodes for this path exist + */ + public boolean nodeExists(final String path) { + ensureRoot(); + Node node = getNodeHelper(path, false, false); + return node != null; + } + + /** + * Creates a new PropBag rooted at the given path, yet sharing the same DOM + * nodes as the creator. + * + * @param path + * Xpath to the root of the new tree. + * @return PropBagEx PropBag rooted at the subtree, or null if the path does not + * exist. + */ + public PropBagMin getSubtree(final String path) { + ensureRoot(); + final Element root = (Element) getNodeHelper(path, false, false); + + if (root == null) { + return null; + } else { + return new PropBagMin(root, true); + } + } + + /** + * Returns the name of the root element. + * + * @return the name of the node. + */ + public String getNodeName() { + ensureRoot(); + return getNodeHelper(BLANK, false, false).getNodeName(); + } + + private Node importNode(final Document doc, final Node n, final boolean bDeep) { + Node dest; + final int type = n.getNodeType(); + switch (type) { + case Node.ELEMENT_NODE: + dest = doc.createElement(n.getNodeName()); + final NamedNodeMap nnm = n.getAttributes(); + final int nnmCount = nnm.getLength(); + for (int i = 0; i < nnmCount; i++) { + final Attr attr = (Attr) nnm.item(i); + ((Element) dest).setAttribute(attr.getName(), attr.getValue()); + } + break; + + case Node.TEXT_NODE: + dest = doc.createTextNode(n.getNodeValue()); + break; + + case Node.CDATA_SECTION_NODE: + dest = doc.createCDATASection(n.getNodeValue()); + break; + + case Node.ENTITY_REFERENCE_NODE: + dest = doc.createEntityReference(n.getNodeValue()); + break; + + // see Jira Defect TLE-1832 : + // http://apps.dytech.com.au/jira/browse/TLE-1832 + case Node.COMMENT_NODE: + dest = doc.createComment(n.getNodeValue()); + break; + + default: + throw new RuntimeException("Unsupported DOM Node: " + type); + } + if (bDeep) { + for (Node child = n.getFirstChild(); child != null; child = child.getNextSibling()) { + dest.appendChild(importNode(doc, child, true)); + } + } + return dest; + } + + private void clear() { + try { + Document doc; + synchronized (factory) { + doc = factory.newDocumentBuilder().newDocument(); + } + + // Create the empty Propbag + final Element root = doc.createElement("xml"); + doc.appendChild(root); + + // Get the root element + m_elRoot = doc.getDocumentElement(); + } catch (final ParserConfigurationException pce) { + throw propagate(pce); + } + } + + private RuntimeException propagate(Throwable t) { + return Throwables.propagate(t); + } +} \ No newline at end of file diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/UnicodeReader.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/UnicodeReader.java new file mode 100644 index 0000000..dc9d06c --- /dev/null +++ b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/UnicodeReader.java @@ -0,0 +1,104 @@ +/* + * Original pseudocode : Thomas Weidenfeller Implementation tweaked: Aki + * Nieminen http://www.unicode.org/unicode/faq/utf_bom.html BOMs: 00 00 FE FF = + * UTF-32, big-endian FF FE 00 00 = UTF-32, little-endian FE FF = UTF-16, + * big-endian FF FE = UTF-16, little-endian EF BB BF = UTF-8 + */ +package org.apereo.openequella.integration.blackboard.common.propbag; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PushbackInputStream; +import java.io.Reader; + +/** + * Generic unicode textreader, which will use BOM mark to identify the encoding + * to be used. + */ +public class UnicodeReader extends Reader { + private static final int BOM_SIZE = 4; + + private PushbackInputStream internalIn; + private InputStreamReader internalIn2 = null; + private String defaultEnc; + + public UnicodeReader(InputStream in, String defaultEnc) { + internalIn = new PushbackInputStream(in, BOM_SIZE); + this.defaultEnc = defaultEnc; + } + + public String getDefaultEncoding() { + return defaultEnc; + } + + public String getEncoding() { + if (internalIn2 == null) { + return null; + } + return internalIn2.getEncoding(); + } + + /** + * Read-ahead four bytes and check for BOM marks. Extra bytes are unread back to + * the stream, only BOM bytes are skipped. + */ + @SuppressWarnings("nls") + protected void init() throws IOException { + if (internalIn2 != null) { + return; + } + + String encoding; + int unread; + byte bom[] = new byte[BOM_SIZE]; + int n = internalIn.read(bom, 0, bom.length); + + if ((bom[0] == (byte) 0xEF) && (bom[1] == (byte) 0xBB) && (bom[2] == (byte) 0xBF)) { + encoding = "UTF-8"; + unread = n - 3; + } else if ((bom[0] == (byte) 0xFE) && (bom[1] == (byte) 0xFF)) { + encoding = "UTF-16BE"; + unread = n - 2; + } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE)) { + encoding = "UTF-16LE"; + unread = n - 2; + } else if ((bom[0] == (byte) 0x00) && (bom[1] == (byte) 0x00) && (bom[2] == (byte) 0xFE) + && (bom[3] == (byte) 0xFF)) { + encoding = "UTF-32BE"; + unread = n - 4; + } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE) && (bom[2] == (byte) 0x00) + && (bom[3] == (byte) 0x00)) { + encoding = "UTF-32LE"; + unread = n - 4; + } else { + // Unicode BOM mark not found, unread all bytes + encoding = defaultEnc; + unread = n; + } + + if (unread > 0) { + internalIn.unread(bom, (n - unread), unread); + } else if (unread < -1) { + internalIn.unread(bom, 0, 0); + // Use given encoding + } + if (encoding == null) { + internalIn2 = new InputStreamReader(internalIn); + } else { + internalIn2 = new InputStreamReader(internalIn, encoding); + } + } + + @Override + public void close() throws IOException { + init(); + internalIn2.close(); + } + + @Override + public int read(char[] cbuf, int off, int len) throws IOException { + init(); + return internalIn2.read(cbuf, off, len); + } +} \ No newline at end of file diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/WsHelper.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/WsHelper.java new file mode 100644 index 0000000..ed6ad54 --- /dev/null +++ b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/WsHelper.java @@ -0,0 +1,69 @@ +package org.apereo.openequella.integration.blackboard.webservice.impl; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Map; +import java.util.Set; + +import org.apereo.openequella.integration.blackboard.webservice.Course; +import org.apereo.openequella.integration.blackboard.webservice.Folder; + +import blackboard.data.content.Content; +import blackboard.persist.Id; +import blackboard.persist.PersistenceException; +import blackboard.platform.security.Entitlement; +import blackboard.platform.security.SecurityUtil; +import blackboard.platform.ws.AxisHelpers; + +public class WsHelper +{ + private static final boolean DEV = false; + + public static void ensurePermission(BbWsSession session, Id courseId, String permission) { + if (!SecurityUtil.userHasEntitlement(session.getUserId(), courseId, new Entitlement(permission))) { + chuckIt(new RuntimeException( + "User does not have " + permission + " entitlement for course " + courseId.toExternalString()), "EQ100"); + } + } + + public static void chuckIt(Throwable t, String code) { + WebServiceUtil.error("Error occurred", t); + String message = t.getMessage(); + if (DEV) { + StringWriter sw = new StringWriter(); + PrintWriter w = new PrintWriter(sw); + t.printStackTrace(w); + message += "
" + sw.toString() + "
"; + } + AxisHelpers.throwWSException(code, message); + } + + public static Folder getFolder(BbWsSession session, Map folderMap, Course course, String folderId) + throws PersistenceException { + Folder folder = folderMap.get(folderId); + if (folder == null) { + // WebServiceUtil.debug("Folder " + folderId + + // " not found in map, loading it"); + Content bbFolder = session.loadContent(folderId); + if (bbFolder != null) { + folder = WebServiceUtil.convertFolder(bbFolder, course.getId()); + folderMap.put(folderId, folder); + } else { + WebServiceUtil.debug("Folder could not be loaded. It doesn't exist any more!"); + } + } + return folder; + } + + public static boolean canCreate(Set coursesWithCreate, Id courseId) { + if (coursesWithCreate == null) { + return true; + } + for (Id id : coursesWithCreate) { + if (id.toExternalString().equals(courseId.toExternalString())) { + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/ZEquellaWebserviceImpl.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/ZEquellaWebserviceImpl.java index 500c16c..9610369 100644 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/ZEquellaWebserviceImpl.java +++ b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/ZEquellaWebserviceImpl.java @@ -1,7 +1,5 @@ package org.apereo.openequella.integration.blackboard.webservice.impl; -import java.io.PrintWriter; -import java.io.StringWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -14,18 +12,6 @@ import javax.jws.WebService; import javax.jws.soap.SOAPBinding; -import blackboard.data.ExtendedData; -import blackboard.data.content.Content; -import blackboard.persist.Id; -import blackboard.persist.PersistenceException; -import blackboard.persist.content.ContentDbLoader; -import blackboard.platform.security.Entitlement; -import blackboard.platform.security.SecurityUtil; -import blackboard.platform.ws.AxisHelpers; -import blackboard.platform.ws.WebserviceContext; -import blackboard.platform.ws.WebserviceException; -import blackboard.platform.ws.anns.AuthenticatedMethod; - import org.apereo.openequella.integration.blackboard.common.BbUtil; import org.apereo.openequella.integration.blackboard.common.content.ContentUtil; import org.apereo.openequella.integration.blackboard.common.content.ItemInfo; @@ -41,6 +27,17 @@ import org.apereo.openequella.integration.blackboard.webservice.Folder; import org.apereo.openequella.integration.blackboard.webservice.SearchResult; +import blackboard.data.ExtendedData; +import blackboard.data.content.Content; +import blackboard.persist.Id; +import blackboard.persist.PersistenceException; +import blackboard.persist.content.ContentDbLoader; +import blackboard.platform.security.Entitlement; +import blackboard.platform.security.SecurityUtil; +import blackboard.platform.ws.WebserviceContext; +import blackboard.platform.ws.WebserviceException; +import blackboard.platform.ws.anns.AuthenticatedMethod; + /** * Why is it called ZEquellaWebserviceImpl? Unfortunately this class must be the * last entry in the zip file, otherwise you are screwed. It is also not allowed @@ -53,8 +50,7 @@ @SOAPBinding(style = SOAPBinding.Style.DOCUMENT, use = SOAPBinding.Use.LITERAL, parameterStyle = SOAPBinding.ParameterStyle.WRAPPED) public class ZEquellaWebserviceImpl implements EquellaWebservice { private static final int CURRENT_EQUELLA_WS_VERSION = 3; - private static final boolean DEV = false; - + // private static final String THIS_WS_NAME = "EQUELLA"; /** @@ -102,7 +98,7 @@ public Course[] listCoursesForUser(@WebParam(mode = WebParam.Mode.IN, name = "us for (blackboard.data.course.Course bbcourse : WebServiceUtil .getBbCourses(modifiableOnly ? session.getUserId() : null)) { if (!modifiableOnly - || canCreate(coursesWithCreate, bbcourse.getId()) && (archived || bbcourse.getIsAvailable())) { + || WsHelper.canCreate(coursesWithCreate, bbcourse.getId()) && (archived || bbcourse.getIsAvailable())) { courses.add(WebServiceUtil.convertCourse(bbcourse)); } } @@ -111,23 +107,11 @@ public Course[] listCoursesForUser(@WebParam(mode = WebParam.Mode.IN, name = "us } catch (WebserviceException ws) { throw ws; } catch (Exception t) { - chuckIt(t, "EQ005"); + WsHelper.chuckIt(t, "EQ005"); return null; } } - private boolean canCreate(Set coursesWithCreate, Id courseId) { - if (coursesWithCreate == null) { - return true; - } - for (Id id : coursesWithCreate) { - if (id.toExternalString().equals(courseId.toExternalString())) { - return true; - } - } - return false; - } - @Override @WebMethod(action = "listFoldersForCourse") @WebResult(name = "folders") @@ -142,7 +126,7 @@ public Folder[] listFoldersForCourse(@WebParam(mode = WebParam.Mode.IN, name = " } catch (WebserviceException ws) { throw ws; } catch (Exception t) { - chuckIt(t, "EQ006"); + WsHelper.chuckIt(t, "EQ006"); return null; } } @@ -163,7 +147,7 @@ public Folder[] listFoldersForFolder(@WebParam(mode = WebParam.Mode.IN, name = " } catch (WebserviceException ws) { throw ws; } catch (Exception t) { - chuckIt(t, "EQ007"); + WsHelper.chuckIt(t, "EQ007"); return null; } } @@ -193,7 +177,7 @@ public AddItemResult addItemToCourse(@WebParam(mode = WebParam.Mode.IN, name = " final Set coursesWithCreate = SecurityUtil.getCourseIdsWithEntitlement(session.getUserId(), new Entitlement("course.content.CREATE")); - if (!canCreate(coursesWithCreate, courseBbId)) { + if (!WsHelper.canCreate(coursesWithCreate, courseBbId)) { throw new PermissionException( "course.content.CREATE not granted for the user " + username + " and course " + courseId); } @@ -223,13 +207,13 @@ public AddItemResult addItemToCourse(@WebParam(mode = WebParam.Mode.IN, name = " } catch (WebserviceException ws) { throw ws; } catch (PermissionException p) { - chuckIt(p, "EQ100"); + WsHelper.chuckIt(p, "EQ100"); return null; } catch (PersistenceException e) { - chuckIt(e, "EQ003"); + WsHelper.chuckIt(e, "EQ003"); return null; } catch (Exception t) { - chuckIt(t, "EQ004"); + WsHelper.chuckIt(t, "EQ004"); return null; } finally { Thread.currentThread().setContextClassLoader(oldLoader); @@ -269,7 +253,7 @@ public SearchResult findUsages(@WebParam(mode = WebParam.Mode.IN, name = "server continue; } - final Folder folder = getFolder(session, folderMap, course, key.getFolderId()); + final Folder folder = WsHelper.getFolder(session, folderMap, course, key.getFolderId()); if (folder == null) { // WebServiceUtil.debug("Unrecording " + key + // " because folder no longer exists"); @@ -288,7 +272,7 @@ public SearchResult findUsages(@WebParam(mode = WebParam.Mode.IN, name = "server } catch (WebserviceException ws) { throw ws; } catch (Exception t) { - chuckIt(t, "EQ011"); + WsHelper.chuckIt(t, "EQ011"); return null; } } @@ -331,7 +315,7 @@ public SearchResult findAllUsages(@WebParam(mode = WebParam.Mode.IN, name = "ser continue; } - final Folder folder = getFolder(session, folderMap, course, key.getFolderId()); + final Folder folder = WsHelper.getFolder(session, folderMap, course, key.getFolderId()); if (folder == null) { continue; } @@ -360,30 +344,13 @@ public SearchResult findAllUsages(@WebParam(mode = WebParam.Mode.IN, name = "ser } catch (WebserviceException ws) { throw ws; } catch (Exception t) { - chuckIt(t, "EQ011"); + WsHelper.chuckIt(t, "EQ011"); return null; } finally { Thread.currentThread().setContextClassLoader(oldLoader); } } - private Folder getFolder(BbWsSession session, Map folderMap, Course course, String folderId) - throws PersistenceException { - Folder folder = folderMap.get(folderId); - if (folder == null) { - // WebServiceUtil.debug("Folder " + folderId + - // " not found in map, loading it"); - Content bbFolder = session.loadContent(folderId); - if (bbFolder != null) { - folder = WebServiceUtil.convertFolder(bbFolder, course.getId()); - folderMap.put(folderId, folder); - } else { - WebServiceUtil.debug("Folder could not be loaded. It doesn't exist any more!"); - } - } - return folder; - } - @Override @WebMethod(action = "synchroniseEquellaContentTables") @WebResult(name = "success") @@ -406,7 +373,7 @@ public String getCourseCode(@WebParam(mode = WebParam.Mode.IN, name = "courseId" } catch (WebserviceException ws) { throw ws; } catch (Exception t) { - chuckIt(t, "EQ012"); + WsHelper.chuckIt(t, "EQ012"); return null; } } @@ -421,7 +388,7 @@ public boolean deleteContent(@WebParam(mode = WebParam.Mode.IN, name = "contentI final BbWsSession session = new BbWsSession(WebserviceContext.getCurrentSession()); if (contentId != null) { final Content content = session.loadContent(contentId); - ensurePermission(session, content.getCourseId(), "course.content.DELETE"); + WsHelper.ensurePermission(session, content.getCourseId(), "course.content.DELETE"); session.deleteContent(contentId); RegistrationUtil.unrecordItem(0, contentId); @@ -434,7 +401,7 @@ public boolean deleteContent(@WebParam(mode = WebParam.Mode.IN, name = "contentI } catch (WebserviceException ws) { throw ws; } catch (Exception t) { - chuckIt(t, "EQ013"); + WsHelper.chuckIt(t, "EQ013"); return false; } finally { Thread.currentThread().setContextClassLoader(oldLoader); @@ -453,7 +420,7 @@ public boolean editContent(@WebParam(mode = WebParam.Mode.IN, name = "contentId" Thread.currentThread().setContextClassLoader(ContentDbLoader.class.getClassLoader()); final BbWsSession session = new BbWsSession(WebserviceContext.getCurrentSession()); final Content content = session.loadContent(contentId); - ensurePermission(session, content.getCourseId(), "course.content.MODIFY"); + WsHelper.ensurePermission(session, content.getCourseId(), "course.content.MODIFY"); final String html; if (ContentUtil.instance().isLegacy(content)) { @@ -475,7 +442,7 @@ public boolean editContent(@WebParam(mode = WebParam.Mode.IN, name = "contentId" } catch (WebserviceException ws) { throw ws; } catch (Exception t) { - chuckIt(t, "EQ014"); + WsHelper.chuckIt(t, "EQ014"); return false; } finally { Thread.currentThread().setContextClassLoader(oldLoader); @@ -494,37 +461,20 @@ public boolean moveContent(@WebParam(mode = WebParam.Mode.IN, name = "contentId" final BbWsSession session = new BbWsSession(WebserviceContext.getCurrentSession()); // Need to ensure privs for source and target course final Content content = session.loadContent(contentId); - ensurePermission(session, content.getCourseId(), "course.content.DELETE"); - ensurePermission(session, session.getCourseId(courseId), "course.content.CREATE"); + WsHelper.ensurePermission(session, content.getCourseId(), "course.content.DELETE"); + WsHelper.ensurePermission(session, session.getCourseId(courseId), "course.content.CREATE"); WebServiceUtil.modifyContent(session, content, courseId, folderId, null, null, null); return true; } catch (WebserviceException ws) { throw ws; } catch (Exception t) { - chuckIt(t, "EQ015"); + WsHelper.chuckIt(t, "EQ015"); return false; } finally { Thread.currentThread().setContextClassLoader(oldLoader); } } - private void ensurePermission(BbWsSession session, Id courseId, String permission) { - if (!SecurityUtil.userHasEntitlement(session.getUserId(), courseId, new Entitlement(permission))) { - chuckIt(new RuntimeException( - "User does not have " + permission + " entitlement for course " + courseId.toExternalString()), "EQ100"); - } - } - private void chuckIt(Throwable t, String code) { - WebServiceUtil.error("Error occurred", t); - String message = t.getMessage(); - if (DEV) { - StringWriter sw = new StringWriter(); - PrintWriter w = new PrintWriter(sw); - t.printStackTrace(w); - message += "
" + sw.toString() + "
"; - } - AxisHelpers.throwWSException(code, message); - } } From 909c949e240078dece433afac3aec52f340990c8 Mon Sep 17 00:00:00 2001 From: Aaron Holland Date: Sun, 10 Feb 2019 17:44:53 +1100 Subject: [PATCH 04/14] #4 format using VS Code default format --- .../blackboard/webservice/impl/WsHelper.java | 78 +++++++++---------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/WsHelper.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/WsHelper.java index ed6ad54..c768370 100644 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/WsHelper.java +++ b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/WsHelper.java @@ -15,55 +15,55 @@ import blackboard.platform.security.SecurityUtil; import blackboard.platform.ws.AxisHelpers; -public class WsHelper -{ +public class WsHelper { private static final boolean DEV = false; public static void ensurePermission(BbWsSession session, Id courseId, String permission) { if (!SecurityUtil.userHasEntitlement(session.getUserId(), courseId, new Entitlement(permission))) { - chuckIt(new RuntimeException( - "User does not have " + permission + " entitlement for course " + courseId.toExternalString()), "EQ100"); + chuckIt(new RuntimeException( + "User does not have " + permission + " entitlement for course " + courseId.toExternalString()), + "EQ100"); } - } - - public static void chuckIt(Throwable t, String code) { + } + + public static void chuckIt(Throwable t, String code) { WebServiceUtil.error("Error occurred", t); String message = t.getMessage(); if (DEV) { - StringWriter sw = new StringWriter(); - PrintWriter w = new PrintWriter(sw); - t.printStackTrace(w); - message += "
" + sw.toString() + "
"; + StringWriter sw = new StringWriter(); + PrintWriter w = new PrintWriter(sw); + t.printStackTrace(w); + message += "
" + sw.toString() + "
"; } AxisHelpers.throwWSException(code, message); - } + } - public static Folder getFolder(BbWsSession session, Map folderMap, Course course, String folderId) - throws PersistenceException { - Folder folder = folderMap.get(folderId); - if (folder == null) { - // WebServiceUtil.debug("Folder " + folderId + - // " not found in map, loading it"); - Content bbFolder = session.loadContent(folderId); - if (bbFolder != null) { - folder = WebServiceUtil.convertFolder(bbFolder, course.getId()); - folderMap.put(folderId, folder); - } else { - WebServiceUtil.debug("Folder could not be loaded. It doesn't exist any more!"); - } - } - return folder; - } + public static Folder getFolder(BbWsSession session, Map folderMap, Course course, String folderId) + throws PersistenceException { + Folder folder = folderMap.get(folderId); + if (folder == null) { + // WebServiceUtil.debug("Folder " + folderId + + // " not found in map, loading it"); + Content bbFolder = session.loadContent(folderId); + if (bbFolder != null) { + folder = WebServiceUtil.convertFolder(bbFolder, course.getId()); + folderMap.put(folderId, folder); + } else { + WebServiceUtil.debug("Folder could not be loaded. It doesn't exist any more!"); + } + } + return folder; + } - public static boolean canCreate(Set coursesWithCreate, Id courseId) { - if (coursesWithCreate == null) { - return true; - } - for (Id id : coursesWithCreate) { - if (id.toExternalString().equals(courseId.toExternalString())) { - return true; - } - } - return false; - } + public static boolean canCreate(Set coursesWithCreate, Id courseId) { + if (coursesWithCreate == null) { + return true; + } + for (Id id : coursesWithCreate) { + if (id.toExternalString().equals(courseId.toExternalString())) { + return true; + } + } + return false; + } } \ No newline at end of file From f87658e812b962a50584965c0da07734ecfa7902 Mon Sep 17 00:00:00 2001 From: C Beach Date: Thu, 14 Feb 2019 14:56:29 -0600 Subject: [PATCH 05/14] #4 - fixed launched by adding isFromAdmin request attribute --- .../blackboard/buildingblock/lti/FixedBasicLtiLauncher.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/lti/FixedBasicLtiLauncher.java b/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/lti/FixedBasicLtiLauncher.java index 358ce30..10168d2 100644 --- a/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/lti/FixedBasicLtiLauncher.java +++ b/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/lti/FixedBasicLtiLauncher.java @@ -164,8 +164,9 @@ public void launch(HttpServletRequest request, HttpServletResponse response, boo request.setAttribute("toolUrl", ourUrl); request.setAttribute("bltiParams", params); - request.setAttribute("useSplashScreen", false); - request.setAttribute("splashScreenMessage", null); + request.setAttribute("useSplashScreen", useSplashScreen); + request.setAttribute("isFromAdmin", false); + request.setAttribute("splashScreenMessage", splashScreenMessage); request.setAttribute("shouldOpenInFrame", true); BbLogger.instance().logTrace( "FixedBasicLtiLauncher.launch: Calling parent launch with url=[" + ourUrl + "], params=[(see below)]", From 8394803f24e0db54a055ac14d23e34b606e1eb7a Mon Sep 17 00:00:00 2001 From: Aaron Holland Date: Fri, 15 Feb 2019 16:56:50 +1100 Subject: [PATCH 06/14] 4 Add fixed postgres code, change namespace from webservice.tle.etc... --- .../lti/FixedBasicLtiLauncher.java | 15 +- .../integration/blackboard/common/BbUtil.java | 8 +- .../blackboard/common/SqlUtil.java | 237 +++++-- .../common/content/RegistrationUtil.java | 638 ++++++++++++------ .../impl/ZEquellaWebserviceImpl.java | 2 +- 5 files changed, 591 insertions(+), 309 deletions(-) diff --git a/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/lti/FixedBasicLtiLauncher.java b/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/lti/FixedBasicLtiLauncher.java index 10168d2..0b65f20 100644 --- a/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/lti/FixedBasicLtiLauncher.java +++ b/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/lti/FixedBasicLtiLauncher.java @@ -11,6 +11,13 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apereo.openequella.integration.blackboard.buildingblock.Configuration; +import org.apereo.openequella.integration.blackboard.buildingblock.data.WrappedContent; +import org.apereo.openequella.integration.blackboard.buildingblock.data.WrappedUser; +import org.apereo.openequella.integration.blackboard.common.BbLogger; +import org.apereo.openequella.integration.blackboard.common.BbUtil; +import org.apereo.openequella.integration.blackboard.common.PathUtils; + import blackboard.base.FormattedText; import blackboard.data.course.CourseMembership; import blackboard.data.user.User; @@ -21,14 +28,6 @@ import blackboard.platform.context.Context; import blackboard.platform.context.ContextManagerFactory; -import com.google.common.base.Throwables; -import org.apereo.openequella.integration.blackboard.buildingblock.Configuration; -import org.apereo.openequella.integration.blackboard.buildingblock.data.WrappedContent; -import org.apereo.openequella.integration.blackboard.buildingblock.data.WrappedUser; -import org.apereo.openequella.integration.blackboard.common.BbLogger; -import org.apereo.openequella.integration.blackboard.common.BbUtil; -import org.apereo.openequella.integration.blackboard.common.PathUtils; - /** * Blackboard doesn't sign query string params!! This needs to be implemented in * such a way so that if BB fixes the issue we aren't double fixing the problem diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/BbUtil.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/BbUtil.java index 3da9e5d..a5aa3ed 100644 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/BbUtil.java +++ b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/BbUtil.java @@ -6,6 +6,9 @@ import java.util.List; import java.util.StringTokenizer; +import com.google.common.base.Strings; +import com.google.common.base.Throwables; + import blackboard.base.BbList; import blackboard.data.content.Content; import blackboard.data.course.Course; @@ -19,14 +22,9 @@ import blackboard.persist.course.CourseDbLoader; import blackboard.persist.navigation.CourseTocDbLoader; import blackboard.platform.config.ConfigurationServiceFactory; -import blackboard.platform.log.LogService; -import blackboard.platform.log.LogServiceFactory; import blackboard.platform.plugin.PlugInUtil; import blackboard.platform.ws.AxisHelpers; -import com.google.common.base.Strings; -import com.google.common.base.Throwables; - /** * @author Aaron */ diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/SqlUtil.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/SqlUtil.java index cdd40e0..c9ab0e8 100644 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/SqlUtil.java +++ b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/SqlUtil.java @@ -16,17 +16,67 @@ */ @SuppressWarnings("nls") // @NonNullByDefault -public class SqlUtil { +public class SqlUtil +{ + public enum DbType + { + UNKNOWN, SQL_SERVER, ORACLE, POSTGRES + } + + private static DbType type = DbType.UNKNOWN; + private static final String TABLE = "equellacontent"; - public static StringBuilder select(String... columns) { + public static DbType getDatabaseType() + { + if( type == DbType.UNKNOWN ) + { + try + { + // Postgres test + runSql("SELECT current_database()", null); + type = DbType.POSTGRES; + } + catch( Exception e ) + { + try + { + // Oracle test + runSql("SELECT 1 FROM DUAL", null); + type = DbType.ORACLE; + } + catch( Exception e2 ) + { + try + { + // SQL server test + runSql("SELECT owner, table_name FROM all_tables", null); + type = DbType.SQL_SERVER; + } + catch( Exception e3 ) + { + type = DbType.UNKNOWN; + } + } + } + } + return type; + } + + public static StringBuilder select(String... columns) + { final StringBuilder sql = new StringBuilder("SELECT "); - if (columns.length == 0) { + if( columns.length == 0 ) + { sql.append("*"); - } else { + } + else + { boolean first = true; - for (String col : columns) { - if (!first) { + for( String col : columns ) + { + if( !first ) + { sql.append(", "); } sql.append(col); @@ -36,11 +86,14 @@ public static StringBuilder select(String... columns) { return sql.append(" ").append(from()); } - public static StringBuilder update(String... columns) { + public static StringBuilder update(String... columns) + { final StringBuilder sql = new StringBuilder("UPDATE ").append(table()).append("SET "); boolean first = true; - for (String col : columns) { - if (!first) { + for( String col : columns ) + { + if( !first ) + { sql.append(", "); } sql.append(col); @@ -50,11 +103,14 @@ public static StringBuilder update(String... columns) { return sql.append(" "); } - public static StringBuilder insert(String... columns) { + public static StringBuilder insert(String... columns) + { StringBuilder sql = new StringBuilder("INSERT INTO ").append(table()).append("("); boolean first = true; - for (String col : columns) { - if (!first) { + for( String col : columns ) + { + if( !first ) + { sql.append(", "); } sql.append(col); @@ -63,130 +119,171 @@ public static StringBuilder insert(String... columns) { return sql.append(") "); } - public static boolean columnExists(String column) { - try { - boolean oracle = DbUtil.safeGetBbDatabase().isOracle(); - SqlUtil.runSql("SELECT " + (oracle ? "" : "TOP 1 ") + " " + column + " FROM " + table(oracle) - + (oracle ? " WHERE rownum < 1" : ""), null); + public static boolean columnExists(String column) + { + try + { + final DbType t = getDatabaseType(); + SqlUtil.runSql("SELECT " + (t == DbType.SQL_SERVER ? "TOP 1 " : "") + " " + column + " FROM " + table() + + (t == DbType.ORACLE ? " WHERE rownum < 1" : (t == DbType.POSTGRES ? " LIMIT 1" : "")), null); return true; - } catch (Exception e) { + } + catch( Exception e ) + { // debug("Failed reading column "+ column +" from DB"); return false; } } - public static String delete() { + public static String delete() + { return "DELETE " + from(); } - public static String from() { + public static String from() + { return "FROM " + table(); } - public static String table(boolean oracle) { - return getSchema(oracle) + TABLE + " "; + public static String table() + { + return schema() + TABLE + " "; } - public static String table() { - return getSchema() + TABLE + " "; + public static String schema() + { + final DbType t = getDatabaseType(); + return (t != DbType.SQL_SERVER ? "" : "dbo."); } - public static String getSchema() { - return getSchema(DbUtil.safeGetBbDatabase().isOracle()); - } - - public static String getSchema(boolean oracle) { - return (oracle ? "" : "dbo."); - } - - public static List runSql(String sql, /* @Nullable */ResultProcessor processor, Object... params) { + public static List runSql(String sql, /* @Nullable */ResultProcessor processor, Object... params) + { PreparedStatement stmt = null; List result = null; - BbLogger.instance().logSqlTrace("Getting connection manager"); + //BbUtil.sqlTrace("Getting connection manager"); ConnectionManager connMgr = DbUtil.safeGetBbDatabase().getConnectionManager(); Connection conn = null; - try { - BbLogger.instance().logSqlTrace("Getting connection"); + try + { + //BbUtil.sqlTrace("Getting connection"); conn = connMgr.getConnection(); - BbLogger.instance().logSqlTrace("Creating statement"); + //BbUtil.sqlTrace("Creating statement"); stmt = conn.prepareStatement(sql); - BbLogger.instance().logSqlTrace("Has " + params.length + " params"); + //BbUtil.sqlTrace("Has " + params.length + " params"); int index = 1; - for (Object param : params) { - if (param instanceof OptionalParam) { + for( Object param : params ) + { + if( param instanceof OptionalParam ) + { final OptionalParam opt = (OptionalParam) param; - if (opt.isUsed()) { + if( opt.isUsed() ) + { setParam(stmt, index++, opt.getValue()); } - } else { + } + else + { setParam(stmt, index++, param); } } - BbLogger.instance().logSqlTrace("Executing: " + sql); - if (processor != null) { + //BbUtil.sqlTrace("Executing: " + sql); + if( processor != null ) + { result = processor.getResults(stmt.executeQuery()); - } else { + } + else + { stmt.execute(); result = (List) Collections.singletonList(stmt.getUpdateCount()); } - BbLogger.instance().logSqlTrace("Success!!"); + //BbUtil.sqlTrace("Success!!"); return result; - } catch (Exception e) { - BbLogger.instance().logError("Failed to runSql", e); - throw new RuntimeException(e); - } finally { - BbLogger.instance().logSqlTrace("Closing statement"); + } + catch( Throwable t ) + { + //BbUtil.error("Failed to runSql", t); + throw new RuntimeException(t); + } + finally + { + //BbUtil.sqlTrace("Closing statement"); DbUtil.closeStatement(stmt); - BbLogger.instance().logSqlTrace("Releasing connection"); + //BbUtil.sqlTrace("Releasing connection"); ConnectionManager.releaseDefaultConnection(conn); } } - public static void setParam(PreparedStatement stmt, int index, /* @Nullable */Object param) throws SQLException { - if (param instanceof String) { - BbLogger.instance().logSqlTrace("Setting param string[" + index + "] = " + param); + public static void setParam(PreparedStatement stmt, int index, /* @Nullable */Object param) throws SQLException + { + if( param instanceof String ) + { + //BbUtil.sqlTrace("Setting param string[" + index + "] = " + param); stmt.setString(index, (String) param); - } else if (param instanceof Integer) { - BbLogger.instance().logSqlTrace("Setting param int[" + index + "] = " + param); + } + else if( param instanceof Integer ) + { + //BbUtil.sqlTrace("Setting param int[" + index + "] = " + param); stmt.setInt(index, (Integer) param); - } else if (param instanceof Timestamp) { - BbLogger.instance().logSqlTrace("Setting param timestamp[" + index + "] = " + param); + } + else if( param instanceof Timestamp ) + { + //BbUtil.sqlTrace("Setting param timestamp[" + index + "] = " + param); stmt.setTimestamp(index, (Timestamp) param); - } else if (param instanceof Boolean) { + } + else if( param instanceof Boolean ) + { boolean pval = (Boolean) param; - BbLogger.instance().logSqlTrace("Setting param boolean[" + index + "] = " + pval); - stmt.setInt(index, pval ? 1 : 0); - } else if (param == null) { - BbLogger.instance().logSqlTrace("Setting param ?[" + index + "] = null"); + //BbUtil.sqlTrace("Setting param boolean[" + index + "] = " + pval); + final SqlUtil.DbType t = SqlUtil.getDatabaseType(); + if (t == DbType.POSTGRES) + { + // TODO: this should work on all types? + stmt.setBoolean(index, pval); + } + else + { + stmt.setInt(index, pval ? 1 : 0); + } + } + else if( param == null ) + { + //BbUtil.sqlTrace("Setting param ?[" + index + "] = null"); stmt.setString(index, null); - } else { + } + else + { throw new RuntimeException("Parameter " + index + " is an unhandled type: " + param.getClass().getName()); } } - public interface ResultProcessor { + public interface ResultProcessor + { List getResults(ResultSet results) throws SQLException; } - public static class OptionalParam { + public static class OptionalParam + { private final T value; private final boolean used; - public OptionalParam(T value, boolean used) { + public OptionalParam(T value, boolean used) + { this.value = value; this.used = used; } - public T getValue() { + public T getValue() + { return value; } - public boolean isUsed() { + public boolean isUsed() + { return used; } } diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/RegistrationUtil.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/RegistrationUtil.java index fa20069..ae5141e 100644 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/RegistrationUtil.java +++ b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/RegistrationUtil.java @@ -12,14 +12,8 @@ import java.util.Date; import java.util.List; -import blackboard.db.BbDatabase; -import blackboard.persist.DatabaseContainer; -import blackboard.platform.persistence.PersistenceServiceFactory; - import com.google.common.base.Strings; -import org.apereo.openequella.integration.blackboard.common.BbLogger; -import org.apereo.openequella.integration.blackboard.common.BbUtil; import org.apereo.openequella.integration.blackboard.common.SqlUtil; import org.apereo.openequella.integration.blackboard.common.SqlUtil.OptionalParam; import org.apereo.openequella.integration.blackboard.common.SqlUtil.ResultProcessor; @@ -30,9 +24,9 @@ */ @SuppressWarnings("nls") // @NonNullByDefault -public abstract class RegistrationUtil { - // until proven otherwise. The databaseVersion is currently 5 (as of after 6.3 - // GA) +public abstract class RegistrationUtil +{ + // until proven otherwise. The databaseVersion is currently 5 (as of after 6.3 GA) private static int databaseVersion = 0; private static final String COL_DATABASE_ID = "ID"; @@ -53,117 +47,146 @@ public abstract class RegistrationUtil { private static final String COL_DATE_ACCESSED = "DTACCESSED"; private static final String COL_ATTACHMENT_NAME = "ATTNAME"; - public RegistrationUtil() { + public RegistrationUtil() + { throw new Error(); } public static void recordItem(ItemKey itemKey, boolean added, boolean available, boolean courseAvailable, - String bbTitle, String bbDesc, String attachmentName, Date dateAdded, Date dateModifed, String courseName) { - BbLogger.instance() - .logTrace("recordItem(" + itemKey + ", " + added + ", " + available + ", " + courseAvailable + ", " + bbTitle - + ", " + bbDesc + ", " + attachmentName + ", " + dateAdded + ", " + dateModifed + ", " + courseName + ")"); + String bbTitle, String bbDesc, String attachmentName, Date dateAdded, Date dateModifed, String courseName) + { + //BbUtil.trace("recordItem(" + itemKey + ", " + added + ", " + available + ", " + courseAvailable + ", " + bbTitle + // + ", " + bbDesc + ", " + attachmentName + ", " + dateAdded + ", " + dateModifed + ", " + courseName + ")"); ensureDatabase(); StringBuilder sql = null; - final boolean oracle = getDatabase().isOracle(); - final String schema = SqlUtil.getSchema(oracle); + final SqlUtil.DbType t = SqlUtil.getDatabaseType(); + final String schema = SqlUtil.schema(); final long now = System.currentTimeMillis(); final java.sql.Timestamp addedTimestamp = new java.sql.Timestamp(dateAdded == null ? now : dateAdded.getTime()); final java.sql.Timestamp modifiedTimestamp = new java.sql.Timestamp( - dateModifed == null ? now : dateModifed.getTime()); - - if (added) { - if (oracle) { - sql = insert(COL_DATABASE_ID, COL_DATE_CREATED, COL_DATE_MODIFIED, COL_INSTITUTION_URL, COL_UUID, COL_VERSION, - COL_COURSE_ID, COL_FOLDER_ID, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, - COL_BLACKBOARD_DESC, COL_PAGE, COL_CONTENT_ID, COL_COURSE_NAME, COL_ATTACHMENT_NAME).append(" VALUES (") - .append(schema).append("equellaseq.nextval, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ,?, ?, ?, ?)"); - } else { - sql = insert(COL_DATE_CREATED, COL_DATE_MODIFIED, COL_INSTITUTION_URL, COL_UUID, COL_VERSION, COL_COURSE_ID, - COL_FOLDER_ID, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC, COL_PAGE, - COL_CONTENT_ID, COL_COURSE_NAME, COL_ATTACHMENT_NAME) - .append(" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + dateModifed == null ? now : dateModifed.getTime()); + + if( added ) + { + if( t == SqlUtil.DbType.ORACLE ) + { + sql = insert(COL_DATABASE_ID, COL_DATE_CREATED, COL_DATE_MODIFIED, COL_INSTITUTION_URL, COL_UUID, + COL_VERSION, COL_COURSE_ID, COL_FOLDER_ID, COL_AVAILABLE, COL_COURSE_AVAILABLE, + COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC, COL_PAGE, COL_CONTENT_ID, COL_COURSE_NAME, + COL_ATTACHMENT_NAME).append(" VALUES (").append(schema) + .append("equellaseq.nextval, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ,?, ?, ?, ?)"); + } + else if( t == SqlUtil.DbType.SQL_SERVER ) + { + sql = insert(COL_DATE_CREATED, COL_DATE_MODIFIED, COL_INSTITUTION_URL, COL_UUID, COL_VERSION, + COL_COURSE_ID, COL_FOLDER_ID, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, + COL_BLACKBOARD_DESC, COL_PAGE, COL_CONTENT_ID, COL_COURSE_NAME, COL_ATTACHMENT_NAME) + .append(" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); } + else if( t == SqlUtil.DbType.POSTGRES ) + { + sql = insert(COL_DATABASE_ID, COL_DATE_CREATED, COL_DATE_MODIFIED, COL_INSTITUTION_URL, COL_UUID, + COL_VERSION, COL_COURSE_ID, COL_FOLDER_ID, COL_AVAILABLE, COL_COURSE_AVAILABLE, + COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC, COL_PAGE, COL_CONTENT_ID, COL_COURSE_NAME, + COL_ATTACHMENT_NAME) + .append(" VALUES (nextval('equellaseq'), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + } + SqlUtil.runSql(sql.toString(), null, addedTimestamp, modifiedTimestamp, itemKey.getInstitutionUrl(), - itemKey.getUuid(), itemKey.getVersion(), itemKey.getCourseId(), itemKey.getFolderId(), available, - courseAvailable, bbTitle, trim(bbDesc, 511), itemKey.getPage(), itemKey.getContentId(), courseName, - attachmentName); + itemKey.getUuid(), itemKey.getVersion(), itemKey.getCourseId(), itemKey.getFolderId(), available, + courseAvailable, bbTitle, trim(bbDesc, 511), itemKey.getPage(), itemKey.getContentId(), courseName, + attachmentName); itemKey.setDatabaseId(0); // would be nice to know what the new one - // is - } else { + // is + } + else + { String contentId = itemKey.getContentId(); - if (!Strings.isNullOrEmpty(contentId)) { - BbLogger.instance().logDebug("updating recorded usage using contentId"); + if( !Strings.isNullOrEmpty(contentId) ) + { + //BbUtil.debug("updating recorded usage using contentId"); - sql = update(COL_DATE_MODIFIED, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC, - COL_PAGE, COL_COURSE_NAME, COL_ATTACHMENT_NAME).append("WHERE CONTENTID=?"); - List updateCount = SqlUtil.runSql(sql.toString(), null, modifiedTimestamp, available, courseAvailable, - bbTitle, bbDesc, itemKey.getPage(), courseName, attachmentName, contentId); + sql = update(COL_DATE_MODIFIED, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, + COL_BLACKBOARD_DESC, COL_PAGE, COL_COURSE_NAME, COL_ATTACHMENT_NAME).append("WHERE CONTENTID=?"); + List updateCount = SqlUtil.runSql(sql.toString(), null, modifiedTimestamp, available, + courseAvailable, bbTitle, bbDesc, itemKey.getPage(), courseName, attachmentName, contentId); int updates = updateCount.get(0); - if (updates > 1) { + if( updates > 1 ) + { // There have instances of dupes. Remove them all. This // should be a rare occurrence - BbLogger.instance().logError("** duplicate recordings of " + contentId + " **", null); + //BbUtil.error("** duplicate recordings of " + contentId + " **", null); // remove dupes and add a brand new entry unrecordItem(0, contentId); - recordItem(itemKey, true, available, courseAvailable, bbTitle, bbDesc, attachmentName, dateAdded, dateModifed, - courseName); - } else if (updates == 0) { - BbLogger.instance().logDebug("content ID not logged... updating the existing one and record the content ID"); + recordItem(itemKey, true, available, courseAvailable, bbTitle, bbDesc, attachmentName, dateAdded, + dateModifed, courseName); + } + else if( updates == 0 ) + { + //BbUtil.debug("content ID not logged... updating the existing one and record the content ID"); sql = update(COL_DATE_MODIFIED, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, - COL_BLACKBOARD_DESC, COL_PAGE, COL_COURSE_NAME, COL_CONTENT_ID, COL_ATTACHMENT_NAME) - .append("WHERE INSTURL=? AND UUID=? AND VERSION=? AND COURSEID=? AND FOLDERID=?"); - updateCount = SqlUtil.runSql(sql.toString(), null, modifiedTimestamp, available, courseAvailable, bbTitle, - trim(bbDesc, 511), itemKey.getPage(), courseName, itemKey.getContentId(), attachmentName, - itemKey.getInstitutionUrl(), itemKey.getUuid(), itemKey.getVersion(), itemKey.getCourseId(), - itemKey.getFolderId()); + COL_BLACKBOARD_DESC, COL_PAGE, COL_COURSE_NAME, COL_CONTENT_ID, COL_ATTACHMENT_NAME) + .append("WHERE INSTURL=? AND UUID=? AND VERSION=? AND COURSEID=? AND FOLDERID=?"); + updateCount = SqlUtil.runSql(sql.toString(), null, modifiedTimestamp, available, courseAvailable, + bbTitle, trim(bbDesc, 511), itemKey.getPage(), courseName, itemKey.getContentId(), + attachmentName, itemKey.getInstitutionUrl(), itemKey.getUuid(), itemKey.getVersion(), + itemKey.getCourseId(), itemKey.getFolderId()); checkSingleUpdate(updateCount); } - } else { - BbLogger.instance().logDebug("updating recorded usage using old field matching"); + } + else + { + //BbUtil.debug("updating recorded usage using old field matching"); - sql = update(COL_DATE_MODIFIED, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC, - COL_PAGE, COL_COURSE_NAME, COL_ATTACHMENT_NAME) - .append("WHERE INSTURL=? AND UUID=? AND VERSION=? AND COURSEID=? AND FOLDERID=?"); + sql = update(COL_DATE_MODIFIED, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, + COL_BLACKBOARD_DESC, COL_PAGE, COL_COURSE_NAME, COL_ATTACHMENT_NAME) + .append("WHERE INSTURL=? AND UUID=? AND VERSION=? AND COURSEID=? AND FOLDERID=?"); final List updateCount = SqlUtil.runSql(sql.toString(), null, modifiedTimestamp, available, - courseAvailable, bbTitle, trim(bbDesc, 511), itemKey.getPage(), courseName, attachmentName, - itemKey.getInstitutionUrl(), itemKey.getUuid(), itemKey.getVersion(), itemKey.getCourseId(), - itemKey.getFolderId()); + courseAvailable, bbTitle, trim(bbDesc, 511), itemKey.getPage(), courseName, attachmentName, + itemKey.getInstitutionUrl(), itemKey.getUuid(), itemKey.getVersion(), itemKey.getCourseId(), + itemKey.getFolderId()); checkSingleUpdate(updateCount); } } } - private static boolean checkSingleUpdate(List updateCount) { + private static boolean checkSingleUpdate(List updateCount) + { final int updates = updateCount.get(0); - if (updates != 1) { + if( updates != 1 ) + { // houston, we have a problem - BbLogger.instance().logError("** Updated " + updates + " rows when only one should have been updated **", null); + //BbUtil.error("** Updated " + updates + " rows when only one should have been updated **", null); return false; } return true; } - private static boolean unrecordItem(int usageId) { - BbLogger.instance().logDebug("unrecordItem(" + usageId + ")"); + private static boolean unrecordItem(int usageId) + { + //BbUtil.debug("unrecordItem(" + usageId + ")"); ensureDatabase(); final List updateCount = SqlUtil.runSql(delete() + "WHERE ID=?", null, usageId); return updateCount.get(0) > 0; } - public static boolean unrecordItem(int usageId, String contentId) { - BbLogger.instance().logDebug("unrecordItem(" + usageId + ", " + contentId + ")"); + public static boolean unrecordItem(int usageId, String contentId) + { + //BbUtil.debug("unrecordItem(" + usageId + ", " + contentId + ")"); ensureDatabase(); - if (usageId != 0) { + if( usageId != 0 ) + { boolean unrecorded = unrecordItem(usageId); - if (unrecorded) { + if( unrecorded ) + { return true; } } @@ -172,63 +195,74 @@ public static boolean unrecordItem(int usageId, String contentId) { return updateCount.get(0) > 0; } - public static boolean updateRecordedTitle(String contentId, String title, String description) { - BbLogger.instance().logDebug("updateRecordedTitle(" + contentId + ", " + title + ", " + description + ")"); + public static boolean updateRecordedTitle(String contentId, String title, String description) + { + //BbUtil.debug("updateRecordedTitle(" + contentId + ", " + title + ", " + description + ")"); ensureDatabase(); final List updateCount = SqlUtil.runSql( - update(COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC).append("WHERE CONTENTID=?").toString(), null, title, - description, contentId); + update(COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC).append("WHERE CONTENTID=?").toString(), null, title, + description, contentId); return updateCount.get(0) > 0; } - public static boolean updateRecordedLocation(String contentId, String courseId, String folderId) { - BbLogger.instance().logDebug("updateRecordedLocation(" + contentId + ", " + courseId + ", " + folderId + ")"); + public static boolean updateRecordedLocation(String contentId, String courseId, String folderId) + { + //BbUtil.debug("updateRecordedLocation(" + contentId + ", " + courseId + ", " + folderId + ")"); ensureDatabase(); final List updateCount = SqlUtil.runSql( - update(COL_COURSE_ID, COL_FOLDER_ID).append("WHERE CONTENTID=?").toString(), null, courseId, folderId, - contentId); + update(COL_COURSE_ID, COL_FOLDER_ID).append("WHERE CONTENTID=?").toString(), null, courseId, folderId, + contentId); return updateCount.get(0) > 0; } - public static boolean updateDateAccessed(String contentId, Date dateAccessed) { - BbLogger.instance().logDebug("updateDateAccessed(" + contentId + ", " + dateAccessed + ")"); + public static boolean updateDateAccessed(String contentId, Date dateAccessed) + { + //BbUtil.debug("updateDateAccessed(" + contentId + ", " + dateAccessed + ")"); ensureDatabase(); final Timestamp accessedTimestamp = new Timestamp(dateAccessed.getTime()); - final List updateCount = SqlUtil.runSql(update(COL_DATE_ACCESSED).append("WHERE CONTENTID=?").toString(), - null, accessedTimestamp, contentId); + final List updateCount = SqlUtil.runSql( + update(COL_DATE_ACCESSED).append("WHERE CONTENTID=?").toString(), null, accessedTimestamp, contentId); return updateCount.get(0) > 0; } public static List findUsages(final String institutionUrl, final String itemUuid, final int itemVersion, - boolean versionIsLatest, boolean allVersions, boolean available) { - BbLogger.instance().logDebug("findUsages(" + institutionUrl + ", " + itemUuid + ", " + itemVersion + ", " - + allVersions + ", " + available + ")"); + boolean versionIsLatest, boolean allVersions, boolean available) + { + //BbUtil.debug("findUsages(" + institutionUrl + ", " + itemUuid + ", " + itemVersion + ", " + allVersions + ", " + // + available + ")"); ensureDatabase(); final StringBuilder sql = select().append("WHERE INSTURL=? AND UUID=?"); - if (!allVersions) { + if( !allVersions ) + { sql.append(" AND (VERSION=?"); - if (versionIsLatest) { + if( versionIsLatest ) + { sql.append(" OR VERSION=0"); } sql.append(")"); } - if (available) { + if( available ) + { sql.append(" AND AVAILABLE=? AND COURSEAVAILABLE=?"); } sql.append(" ORDER BY DTCREATED DESC"); - final List usages = SqlUtil.runSql(sql.toString(), new ResultProcessor() { + final List usages = SqlUtil.runSql(sql.toString(), new ResultProcessor() + { @Override - public List getResults(ResultSet results) throws SQLException { + public List getResults(ResultSet results) throws SQLException + { final List u = new ArrayList(); - while (results.next()) { + while( results.next() ) + { final String contentId = results.getString(COL_CONTENT_ID); final ItemInfo r = new ItemInfo(institutionUrl, itemUuid, results.getInt(COL_VERSION), contentId, - results.getString(COL_COURSE_ID), results.getString(COL_FOLDER_ID), results.getString(COL_PAGE)); + results.getString(COL_COURSE_ID), results.getString(COL_FOLDER_ID), + results.getString(COL_PAGE)); r.setCreatedDate(results.getTimestamp(COL_DATE_CREATED)); r.setModifiedDate(results.getTimestamp(COL_DATE_MODIFIED)); r.setAvailable(results.getBoolean(COL_AVAILABLE)); @@ -241,94 +275,123 @@ public List getResults(ResultSet results) throws SQLException { return u; } }, institutionUrl, itemUuid, new OptionalParam(itemVersion, !allVersions), - new OptionalParam(available, available), new OptionalParam(available, available)); + new OptionalParam(available, available), new OptionalParam(available, available)); return usages; } public static List findAllUsages(final String institutionUrl, final String query, final String courseId, - String folderId, boolean available, String sortColumn, boolean sortReverse) { - BbLogger.instance().logDebug("findAllUsages(" + institutionUrl + ", " + query + ", " + courseId + ", " + folderId - + ", " + available + ", " + sortColumn + ", " + sortReverse + ")"); + String folderId, boolean available, String sortColumn, boolean sortReverse) + { + //BbUtil.debug("findAllUsages(" + institutionUrl + ", " + query + ", " + courseId + ", " + folderId + ", " + // + available + ", " + sortColumn + ", " + sortReverse + ")"); ensureDatabase(); final StringBuilder sql = select().append(" WHERE INSTURL=?"); final boolean hasQuery = !Strings.isNullOrEmpty(query); String likeQuery = null; - if (hasQuery) { + if( hasQuery ) + { likeQuery = '%' + query.toLowerCase() + '%'; sql.append(" AND LOWER(BBTITLE) LIKE ?"); } final boolean hasCourseId = !Strings.isNullOrEmpty(courseId); - if (hasCourseId) { + if( hasCourseId ) + { sql.append(" AND COURSEID = ?"); } final boolean hasFolderId = !Strings.isNullOrEmpty(folderId); - if (hasFolderId) { + if( hasFolderId ) + { sql.append(" AND FOLDERID = ?"); } - if (available) { + if( available ) + { sql.append(" AND AVAILABLE=? AND COURSEAVAILABLE=?"); } String sortCol = "DTCREATED"; String sortOrd = sortReverse ? "DESC" : "ASC"; - if (!Strings.isNullOrEmpty(sortColumn)) { - if (sortColumn.equals("name")) { + if( !Strings.isNullOrEmpty(sortColumn) ) + { + if( sortColumn.equals("name") ) + { sortCol = "LOWER(BBTITLE)"; - } else if (sortColumn.equals("course")) { + } + else if( sortColumn.equals("course") ) + { sortCol = "LOWER(COURSENAME)"; } } sql.append(" ORDER BY " + sortCol + " " + sortOrd); - // @formatter:off - final List usages = SqlUtil.runSql(sql.toString(), new ResultProcessor() { - @Override - public List getResults(ResultSet results) throws SQLException { - final List u = new ArrayList(); - while (results.next()) { - final ItemInfo r = new ItemInfo(institutionUrl, results.getString(COL_UUID), results.getInt(COL_VERSION), - results.getString(COL_CONTENT_ID), results.getString(COL_COURSE_ID), results.getString(COL_FOLDER_ID), - results.getString(COL_PAGE)); - r.setCreatedDate(results.getTimestamp(COL_DATE_CREATED)); - r.setModifiedDate(results.getTimestamp(COL_DATE_MODIFIED)); - r.setAvailable(results.getBoolean(COL_AVAILABLE)); - r.setName(results.getString(COL_BLACKBOARD_TITLE)); - r.setDescription(results.getString(COL_BLACKBOARD_DESC)); - r.getItemKey().setDatabaseId(results.getInt(COL_DATABASE_ID)); - r.setDateAccessed(results.getTimestamp(COL_DATE_ACCESSED)); - r.setAttachmentName(results.getString(COL_ATTACHMENT_NAME)); - u.add(r); - } - return u; - } - }, institutionUrl, new OptionalParam(likeQuery, hasQuery), new OptionalParam(courseId, hasCourseId), - new OptionalParam(folderId, hasFolderId), new OptionalParam(available, available), - new OptionalParam(available, available)); - // @formatter:on + //@formatter:off + final List usages = SqlUtil.runSql( + sql.toString(), + new ResultProcessor() + { + @Override + public List getResults(ResultSet results) throws SQLException + { + final List u = new ArrayList(); + while( results.next() ) + { + final ItemInfo r = new ItemInfo( + institutionUrl, + results.getString(COL_UUID), + results.getInt(COL_VERSION), + results.getString(COL_CONTENT_ID), + results.getString(COL_COURSE_ID), + results.getString(COL_FOLDER_ID), + results.getString(COL_PAGE) + ); + r.setCreatedDate(results.getTimestamp(COL_DATE_CREATED)); + r.setModifiedDate(results.getTimestamp(COL_DATE_MODIFIED)); + r.setAvailable(results.getBoolean(COL_AVAILABLE)); + r.setName(results.getString(COL_BLACKBOARD_TITLE)); + r.setDescription(results.getString(COL_BLACKBOARD_DESC)); + r.getItemKey().setDatabaseId(results.getInt(COL_DATABASE_ID)); + r.setDateAccessed(results.getTimestamp(COL_DATE_ACCESSED)); + r.setAttachmentName(results.getString(COL_ATTACHMENT_NAME)); + u.add(r); + } + return u; + } + }, + institutionUrl, + new OptionalParam(likeQuery, hasQuery), + new OptionalParam(courseId, hasCourseId), + new OptionalParam(folderId, hasFolderId), + new OptionalParam(available, available), + new OptionalParam(available, available) + ); + //@formatter:on return usages; } // Used by SynchroniseContentThread public static List findEquellaContentByCourse(final String institutionUrl, final String courseId, - boolean available) { - BbLogger.instance() - .logDebug("findEquellaContentByCourse(" + institutionUrl + ", " + courseId + ", " + available + ")"); + boolean available) + { + //BbUtil.debug("findEquellaContentByCourse(" + institutionUrl + ", " + courseId + ", " + available + ")"); ensureDatabase(); // ID, UUID, VERSION, FOLDERID, ATTACHMENT_NAME, CONTENTID - final StringBuilder sql = select(COL_DATABASE_ID, COL_UUID, COL_VERSION, COL_FOLDER_ID, COL_PAGE, COL_CONTENT_ID) - .append("WHERE INSTURL=? AND COURSEID=?"); - if (available) { + final StringBuilder sql = select(COL_DATABASE_ID, COL_UUID, COL_VERSION, COL_FOLDER_ID, COL_PAGE, + COL_CONTENT_ID).append("WHERE INSTURL=? AND COURSEID=?"); + if( available ) + { sql.append(" AND AVAILABLE=? AND COURSEAVAILABLE=?"); } - final List usages = SqlUtil.runSql(sql.toString(), new ResultProcessor() { + final List usages = SqlUtil.runSql(sql.toString(), new ResultProcessor() + { @Override - public List getResults(ResultSet results) throws SQLException { + public List getResults(ResultSet results) throws SQLException + { List u = new ArrayList(); - while (results.next()) { + while( results.next() ) + { final int dbId = results.getInt(COL_DATABASE_ID); final String uuid = results.getString(COL_UUID); final int version = results.getInt(COL_VERSION); @@ -343,12 +406,13 @@ public List getResults(ResultSet results) throws SQLException { return u; } }, institutionUrl, courseId, new OptionalParam(available, available), - new OptionalParam(available, available)); + new OptionalParam(available, available)); return usages; } - public static int cleanupBadContent(final String institutionUrl) { - BbLogger.instance().logDebug("cleanupBadContent(" + institutionUrl + ")"); + public static int cleanupBadContent(final String institutionUrl) + { + //BbUtil.debug("cleanupBadContent(" + institutionUrl + ")"); ensureDatabase(); String sql = delete() + "WHERE INSTURL=? AND CONTENTID IS NULL"; @@ -356,17 +420,21 @@ public static int cleanupBadContent(final String institutionUrl) { return updates.get(0); } - public static ContentRegisteredResponse contentIsRegistered(ItemKey itemKey) { - BbLogger.instance().logDebug("contentIsRegistered(" + itemKey + ")"); + public static ContentRegisteredResponse contentIsRegistered(ItemKey itemKey) + { + //BbUtil.debug("contentIsRegistered(" + itemKey + ")"); ensureDatabase(); - final ResultProcessor resultProcessor = new ResultProcessor() { + final ResultProcessor resultProcessor = new ResultProcessor() + { @Override - public List getResults(ResultSet results) throws SQLException { + public List getResults(ResultSet results) throws SQLException + { List r = new ArrayList(); - while (results.next()) { - r.add(new Object[] { results.getInt(COL_DATABASE_ID), results.getBoolean(COL_AVAILABLE), - results.getString(COL_CONTENT_ID) }); + while( results.next() ) + { + r.add(new Object[]{results.getInt(COL_DATABASE_ID), results.getBoolean(COL_AVAILABLE), + results.getString(COL_CONTENT_ID)}); } return r; } @@ -374,22 +442,26 @@ public List getResults(ResultSet results) throws SQLException { final List res; final StringBuilder sql = select(COL_DATABASE_ID, COL_AVAILABLE, COL_CONTENT_ID); - if (itemKey.getContentId() != null) { + if( itemKey.getContentId() != null ) + { sql.append("WHERE CONTENTID=?"); res = SqlUtil.runSql(sql.toString(), resultProcessor, itemKey.getContentId()); - } else { + } + else + { // OLD STYLE final String page = itemKey.getPage(); sql.append("WHERE INSTURL=? AND UUID=? AND VERSION=? AND COURSEID=? AND FOLDERID=? AND PAGE" - + (Strings.isNullOrEmpty(page) ? " IS NULL" : "=?")); + + (Strings.isNullOrEmpty(page) ? " IS NULL" : "=?")); res = SqlUtil.runSql(sql.toString(), resultProcessor, itemKey.getInstitutionUrl(), itemKey.getUuid(), - itemKey.getVersion(), itemKey.getCourseId(), itemKey.getFolderId(), - new OptionalParam(page, !Strings.isNullOrEmpty(page))); + itemKey.getVersion(), itemKey.getCourseId(), itemKey.getFolderId(), + new OptionalParam(page, !Strings.isNullOrEmpty(page))); } final ContentRegisteredResponse response = new ContentRegisteredResponse(); - if (!res.isEmpty()) { + if( !res.isEmpty() ) + { response.setRegistered(true); final Object[] r = res.get(0); response.setId((Integer) r[0]); @@ -400,40 +472,53 @@ public List getResults(ResultSet results) throws SQLException { return response; } - private static void ensureDatabase() { - if (databaseVersion < 1) { - try { + private static void ensureDatabase() + { + if( databaseVersion < 1 ) + { + try + { SqlUtil.runSql(SqlUtil.select("1").toString(), null); databaseVersion = 1; - } catch (Exception e) { + } + catch( Exception e ) + { makeDatabase(); databaseVersion = 1; } } - if (databaseVersion < 2) { - if (!SqlUtil.columnExists(COL_CONTENT_ID)) { + if( databaseVersion < 2 ) + { + if( !SqlUtil.columnExists(COL_CONTENT_ID) ) + { modifyDatabaseAddContentAndCourseName(); } databaseVersion = 2; } - if (databaseVersion < 3) { - if (!SqlUtil.columnExists(COL_BLACKBOARD_DESC)) { + if( databaseVersion < 3 ) + { + if( !SqlUtil.columnExists(COL_BLACKBOARD_DESC) ) + { modifyDatabaseAddDescription(); } databaseVersion = 3; } - if (databaseVersion < 4) { - if (!SqlUtil.columnExists(COL_DATE_ACCESSED)) { + if( databaseVersion < 4 ) + { + if( !SqlUtil.columnExists(COL_DATE_ACCESSED) ) + { modifyDatabaseAddDateAccessed(); } databaseVersion = 4; } - if (databaseVersion < 5) { - if (!SqlUtil.columnExists(COL_ATTACHMENT_NAME)) { + if( databaseVersion < 5 ) + { + if( !SqlUtil.columnExists(COL_ATTACHMENT_NAME) ) + { modifyDatabaseAddAttachmentName(); } databaseVersion = 5; @@ -443,111 +528,214 @@ private static void ensureDatabase() { /** * Todo: this clearly isn't normalised. But the question is, is it worth it? */ - private static void makeDatabase() { - final String sql; - if (getDatabase().isOracle()) { - BbLogger.instance().logDebug("Oracle DB detected"); + private static void makeDatabase() + { + final SqlUtil.DbType t = SqlUtil.getDatabaseType(); + if( t == SqlUtil.DbType.ORACLE ) + { + //BbUtil.debug("Oracle DB detected"); SqlUtil.runSql("CREATE SEQUENCE equellaseq START WITH 1 INCREMENT BY 1 NOMAXVALUE", null); - // @formatter:off - sql = "CREATE TABLE equellacontent (" + "ID NUMBER NOT NULL, " + "INSTURL NVARCHAR2(255) NOT NULL, " - + "DTCREATED DATE DEFAULT SYSDATE, " + "DTMODIFIED DATE DEFAULT SYSDATE, " - + "BBTITLE NVARCHAR2(1024) NOT NULL," + "PAGE NVARCHAR2(1024)," // nullable for item summaries - + "UUID NVARCHAR2(40) NOT NULL, " + "VERSION NUMBER NOT NULL," + "COURSEID NVARCHAR2(32) NOT NULL, " - + "FOLDERID NVARCHAR2(32) NOT NULL, " + "AVAILABLE NUMBER NOT NULL," + "COURSEAVAILABLE NUMBER NOT NULL," - + "CONSTRAINT equellacontent_pk PRIMARY KEY (ID))"; - // @formatter:on + //@formatter:off + final String sql = "CREATE TABLE equellacontent (" + + "ID NUMBER NOT NULL, " + + "INSTURL NVARCHAR2(255) NOT NULL, " + + "DTCREATED DATE DEFAULT SYSDATE, " + + "DTMODIFIED DATE DEFAULT SYSDATE, " + + "BBTITLE NVARCHAR2(1024) NOT NULL," + + "PAGE NVARCHAR2(1024)," //nullable for item summaries + + "UUID NVARCHAR2(40) NOT NULL, " + + "VERSION NUMBER NOT NULL," + + "COURSEID NVARCHAR2(32) NOT NULL, " + + "FOLDERID NVARCHAR2(32) NOT NULL, " + + "AVAILABLE NUMBER NOT NULL," + + "COURSEAVAILABLE NUMBER NOT NULL," + + "CONSTRAINT equellacontent_pk PRIMARY KEY (ID))"; + //@formatter:on SqlUtil.runSql(sql, null); SqlUtil.runSql("CREATE INDEX eqcontent_uuidversion ON equellacontent(INSTURL, UUID, VERSION)", null); // cannot be UNIQUE since same item could be added to same folder // (in theory) - SqlUtil.runSql("CREATE INDEX eqcontent_all ON equellacontent(INSTURL, UUID, VERSION, COURSEID, FOLDERID)", null); + SqlUtil.runSql("CREATE INDEX eqcontent_all ON equellacontent(INSTURL, UUID, VERSION, COURSEID, FOLDERID)", + null); SqlUtil.runSql("CREATE INDEX eqcontent_course ON equellacontent(INSTURL, COURSEID)", null); SqlUtil.runSql("CREATE INDEX eqcontent_available ON equellacontent(AVAILABLE, COURSEAVAILABLE)", null); - } else { - BbLogger.instance().logDebug("SQL Server DB detected"); - // @formatter:off - sql = "CREATE TABLE dbo.equellacontent (" + "ID int NOT NULL PRIMARY KEY IDENTITY, " - + "INSTURL nvarchar(255) NOT NULL, " + "BBTITLE nvarchar(1024) NOT NULL, " + "PAGE nvarchar(1024) NULL, " // nullable - // for - // item - // summaries - + "DTCREATED datetime NOT NULL, " + "DTMODIFIED datetime NOT NULL, " + "UUID nvarchar(40) NOT NULL, " - + "VERSION int NOT NULL," + "COURSEID nvarchar(32) NOT NULL, " + "FOLDERID nvarchar(32) NOT NULL, " - + "AVAILABLE bit NOT NULL," + "COURSEAVAILABLE bit NOT NULL" + ")"; - // @formatter:on + } + else if( t == SqlUtil.DbType.SQL_SERVER ) + { + //BbUtil.debug("SQL Server DB detected"); + + //@formatter:off + final String sql = "CREATE TABLE dbo.equellacontent (" + + "ID int NOT NULL PRIMARY KEY IDENTITY, " + + "INSTURL nvarchar(255) NOT NULL, " + + "BBTITLE nvarchar(1024) NOT NULL, " + + "PAGE nvarchar(1024) NULL, " //nullable for item summaries + + "DTCREATED datetime NOT NULL, " + + "DTMODIFIED datetime NOT NULL, " + + "UUID nvarchar(40) NOT NULL, " + + "VERSION int NOT NULL," + + "COURSEID nvarchar(32) NOT NULL, " + + "FOLDERID nvarchar(32) NOT NULL, " + + "AVAILABLE bit NOT NULL," + + "COURSEAVAILABLE bit NOT NULL" + + ")"; + //@formatter:on SqlUtil.runSql(sql, null); SqlUtil.runSql("CREATE INDEX eqcontent_course ON dbo.equellacontent(INSTURL, COURSEID)", null); // cannot be UNIQUE since same item could be added to same folder // (in theory) - SqlUtil.runSql("CREATE INDEX eqcontent_all ON dbo.equellacontent(INSTURL, UUID, VERSION, COURSEID, FOLDERID)", - null); + SqlUtil.runSql( + "CREATE INDEX eqcontent_all ON dbo.equellacontent(INSTURL, UUID, VERSION, COURSEID, FOLDERID)", null); SqlUtil.runSql("CREATE INDEX eqcontent_uuidversion ON dbo.equellacontent(INSTURL, UUID, VERSION)", null); SqlUtil.runSql("CREATE INDEX eqcontent_available ON dbo.equellacontent(AVAILABLE, COURSEAVAILABLE)", null); } + else if( t == SqlUtil.DbType.POSTGRES ) + { + //BbUtil.debug("Postgres DB detected"); + + try + { + SqlUtil.runSql("CREATE SEQUENCE equellaseq START WITH 1 INCREMENT BY 1 NO MAXVALUE", null); + } + catch( Exception e ) + { + // Ignore, it may already exist + } + + //@formatter:off + final String sql = "CREATE TABLE equellacontent (" + + "ID int NOT NULL PRIMARY KEY, " + + "INSTURL varchar(255) NOT NULL, " + + "BBTITLE varchar(1024) NOT NULL, " + + "PAGE varchar(1024) NULL, " //nullable for item summaries + + "DTCREATED timestamp without time zone NOT NULL, " + + "DTMODIFIED timestamp without time zone NOT NULL, " + + "UUID varchar(40) NOT NULL, " + + "VERSION int NOT NULL," + + "COURSEID varchar(32) NOT NULL, " + + "FOLDERID varchar(32) NOT NULL, " + + "AVAILABLE boolean NOT NULL," + + "COURSEAVAILABLE boolean NOT NULL" + + ")"; + //@formatter:on + SqlUtil.runSql(sql, null); + + SqlUtil.runSql("CREATE INDEX eqcontent_course ON equellacontent(INSTURL, COURSEID)", null); + // cannot be UNIQUE since same item could be added to same folder + // (in theory) + SqlUtil.runSql("CREATE INDEX eqcontent_all ON equellacontent(INSTURL, UUID, VERSION, COURSEID, FOLDERID)", + null); + SqlUtil.runSql("CREATE INDEX eqcontent_uuidversion ON equellacontent(INSTURL, UUID, VERSION)", null); + SqlUtil.runSql("CREATE INDEX eqcontent_available ON equellacontent(AVAILABLE, COURSEAVAILABLE)", null); + } } - private static void modifyDatabaseAddContentAndCourseName() { - if (getDatabase().isOracle()) { + private static void modifyDatabaseAddContentAndCourseName() + { + final SqlUtil.DbType t = SqlUtil.getDatabaseType(); + if( t == SqlUtil.DbType.ORACLE ) + { // debug("Oracle DB detected"); - // @formatter:off - final String sql = "ALTER TABLE equellacontent ADD (" + "CONTENTID NVARCHAR2(32) NULL, " - + "COURSENAME NVARCHAR2(512) NULL" + ")"; - // @formatter:on + //@formatter:off + final String sql = "ALTER TABLE equellacontent ADD (" + + "CONTENTID NVARCHAR2(32) NULL, " + + "COURSENAME NVARCHAR2(512) NULL" + + ")"; + //@formatter:on SqlUtil.runSql(sql, null); SqlUtil.runSql("CREATE INDEX eqcontent_content ON equellacontent(INSTURL, CONTENTID)", null); - } else { + } + else if( t == SqlUtil.DbType.SQL_SERVER ) + { // debug("SQL Server DB detected"); - // @formatter:off - final String sql = "ALTER TABLE dbo.equellacontent ADD " + "CONTENTID nvarchar(32) NULL, " - + "COURSENAME nvarchar(512) NULL"; - // @formatter:on + //@formatter:off + final String sql = "ALTER TABLE dbo.equellacontent ADD " + + "CONTENTID nvarchar(32) NULL, " + + "COURSENAME nvarchar(512) NULL"; + //@formatter:on SqlUtil.runSql(sql, null); SqlUtil.runSql("CREATE INDEX eqcontent_content ON dbo.equellacontent(INSTURL, CONTENTID)", null); } + else if( t == SqlUtil.DbType.POSTGRES ) + { + // N/A. There won't be any Postgres DBs this old + } } - private static void modifyDatabaseAddDescription() { - final String sql; - if (getDatabase().isOracle()) { + private static void modifyDatabaseAddDescription() + { + final SqlUtil.DbType t = SqlUtil.getDatabaseType(); + String sql = null; + if( t == SqlUtil.DbType.ORACLE ) + { sql = "ALTER TABLE equellacontent ADD (BBDESC NVARCHAR2(512) NULL)"; - } else { + } + else if( t == SqlUtil.DbType.SQL_SERVER ) + { sql = "ALTER TABLE dbo.equellacontent ADD BBDESC nvarchar(512) NULL"; } - SqlUtil.runSql(sql, null); + else if( t == SqlUtil.DbType.POSTGRES ) + { + // N/A. There won't be any Postgres DBs this old + } + if( sql != null ) + { + SqlUtil.runSql(sql, null); + } } - private static void modifyDatabaseAddDateAccessed() { - final String sql; - if (getDatabase().isOracle()) { + private static void modifyDatabaseAddDateAccessed() + { + final SqlUtil.DbType t = SqlUtil.getDatabaseType(); + String sql = null; + if( t == SqlUtil.DbType.ORACLE ) + { sql = "ALTER TABLE equellacontent ADD (" + COL_DATE_ACCESSED + " DATE NULL)"; - } else { + } + else if( t == SqlUtil.DbType.SQL_SERVER ) + { sql = "ALTER TABLE dbo.equellacontent ADD " + COL_DATE_ACCESSED + " datetime NULL"; } - SqlUtil.runSql(sql, null); + else if( t == SqlUtil.DbType.POSTGRES ) + { + // N/A. There won't be any Postgres DBs this old + } + if( sql != null ) + { + SqlUtil.runSql(sql, null); + } } - private static void modifyDatabaseAddAttachmentName() { - final String sql; - if (getDatabase().isOracle()) { + private static void modifyDatabaseAddAttachmentName() + { + final SqlUtil.DbType t = SqlUtil.getDatabaseType(); + String sql = null; + if( t == SqlUtil.DbType.ORACLE ) + { sql = "ALTER TABLE equellacontent ADD (" + COL_ATTACHMENT_NAME + " NVARCHAR2(512) NULL)"; - } else { + } + else if( t == SqlUtil.DbType.SQL_SERVER ) + { sql = "ALTER TABLE dbo.equellacontent ADD " + COL_ATTACHMENT_NAME + " nvarchar(512) NULL"; } - SqlUtil.runSql(sql, null); - } - - private static BbDatabase getDatabase() { - final DatabaseContainer dbContainer = (DatabaseContainer) PersistenceServiceFactory.getInstance() - .getDbPersistenceManager().getContainer(); - return dbContainer.getBbDatabase(); + else if( t == SqlUtil.DbType.POSTGRES ) + { + // N/A. There won't be any Postgres DBs this old + } + if( sql != null ) + { + SqlUtil.runSql(sql, null); + } } - private static String trim(String text, int maxlen) { - if (text != null && text.length() > maxlen) { + private static String trim(String text, int maxlen) + { + if( text != null && text.length() > maxlen ) + { return text.substring(0, maxlen); } return text; diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/ZEquellaWebserviceImpl.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/ZEquellaWebserviceImpl.java index 9610369..4d71ce9 100644 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/ZEquellaWebserviceImpl.java +++ b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/ZEquellaWebserviceImpl.java @@ -46,7 +46,7 @@ * @author Aaron */ @SuppressWarnings("nls") -@WebService(name = "EQUELLA", serviceName = "EQUELLA", portName = "WS", targetNamespace = "http://webservice.blackboard.tle.com") +@WebService(name = "EQUELLA", serviceName = "EQUELLA", portName = "WS", targetNamespace = "http://webservice.blackboard.integration.openequella.apereo.org") @SOAPBinding(style = SOAPBinding.Style.DOCUMENT, use = SOAPBinding.Use.LITERAL, parameterStyle = SOAPBinding.ParameterStyle.WRAPPED) public class ZEquellaWebserviceImpl implements EquellaWebservice { private static final int CURRENT_EQUELLA_WS_VERSION = 3; From 9ef721a20ffe08fe1993211937a6763616e9d1c7 Mon Sep 17 00:00:00 2001 From: Aaron Holland Date: Sun, 17 Feb 2019 11:56:53 +1100 Subject: [PATCH 07/14] 4 refactor webservice --- .../blackboard/webservice/AddItemResult.java | 2 +- .../tle}/blackboard/webservice/Base.java | 2 +- .../tle}/blackboard/webservice/Content.java | 2 +- .../tle}/blackboard/webservice/Course.java | 2 +- .../webservice/EquellaWebservice.java | 2 +- .../tle}/blackboard/webservice/Folder.java | 2 +- .../blackboard/webservice/SearchResult.java | 2 +- .../webservice/impl/BbWsSession.java | 8 ++-- .../webservice/impl/PermissionException.java | 2 +- .../impl/SynchroniseContentThread.java | 12 +++--- .../webservice/impl/WebServiceUtil.java | 39 ++++++++++--------- .../blackboard/webservice/impl/WsHelper.java | 6 +-- .../impl/ZEquellaWebserviceImpl.java | 25 ++++++------ 13 files changed, 54 insertions(+), 52 deletions(-) rename oeqPrimaryWS/src/main/java/{org/apereo/openequella/integration => com/tle}/blackboard/webservice/AddItemResult.java (83%) rename oeqPrimaryWS/src/main/java/{org/apereo/openequella/integration => com/tle}/blackboard/webservice/Base.java (90%) rename oeqPrimaryWS/src/main/java/{org/apereo/openequella/integration => com/tle}/blackboard/webservice/Content.java (97%) rename oeqPrimaryWS/src/main/java/{org/apereo/openequella/integration => com/tle}/blackboard/webservice/Course.java (92%) rename oeqPrimaryWS/src/main/java/{org/apereo/openequella/integration => com/tle}/blackboard/webservice/EquellaWebservice.java (98%) rename oeqPrimaryWS/src/main/java/{org/apereo/openequella/integration => com/tle}/blackboard/webservice/Folder.java (92%) rename oeqPrimaryWS/src/main/java/{org/apereo/openequella/integration => com/tle}/blackboard/webservice/SearchResult.java (91%) rename oeqPrimaryWS/src/main/java/{org/apereo/openequella/integration => com/tle}/blackboard/webservice/impl/BbWsSession.java (98%) rename oeqPrimaryWS/src/main/java/{org/apereo/openequella/integration => com/tle}/blackboard/webservice/impl/PermissionException.java (70%) rename oeqPrimaryWS/src/main/java/{org/apereo/openequella/integration => com/tle}/blackboard/webservice/impl/SynchroniseContentThread.java (99%) rename oeqPrimaryWS/src/main/java/{org/apereo/openequella/integration => com/tle}/blackboard/webservice/impl/WebServiceUtil.java (89%) rename oeqPrimaryWS/src/main/java/{org/apereo/openequella/integration => com/tle}/blackboard/webservice/impl/WsHelper.java (90%) rename oeqPrimaryWS/src/main/java/{org/apereo/openequella/integration => com/tle}/blackboard/webservice/impl/ZEquellaWebserviceImpl.java (94%) diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/AddItemResult.java b/oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/AddItemResult.java similarity index 83% rename from oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/AddItemResult.java rename to oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/AddItemResult.java index b21cd5e..e532dc3 100644 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/AddItemResult.java +++ b/oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/AddItemResult.java @@ -1,4 +1,4 @@ -package org.apereo.openequella.integration.blackboard.webservice; +package com.tle.blackboard.webservice; public class AddItemResult { private Folder folder; diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/Base.java b/oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/Base.java similarity index 90% rename from oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/Base.java rename to oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/Base.java index 601b3c4..9ac35cd 100644 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/Base.java +++ b/oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/Base.java @@ -1,4 +1,4 @@ -package org.apereo.openequella.integration.blackboard.webservice; +package com.tle.blackboard.webservice; public class Base { private String id; diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/Content.java b/oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/Content.java similarity index 97% rename from oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/Content.java rename to oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/Content.java index 04eeef2..e86f9b9 100644 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/Content.java +++ b/oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/Content.java @@ -1,4 +1,4 @@ -package org.apereo.openequella.integration.blackboard.webservice; +package com.tle.blackboard.webservice; public class Content { private String id; diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/Course.java b/oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/Course.java similarity index 92% rename from oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/Course.java rename to oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/Course.java index 71b2b07..afddfc8 100644 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/Course.java +++ b/oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/Course.java @@ -1,4 +1,4 @@ -package org.apereo.openequella.integration.blackboard.webservice; +package com.tle.blackboard.webservice; public class Course extends Base { private boolean available; diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/EquellaWebservice.java b/oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/EquellaWebservice.java similarity index 98% rename from oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/EquellaWebservice.java rename to oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/EquellaWebservice.java index e1d45a3..fced012 100644 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/EquellaWebservice.java +++ b/oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/EquellaWebservice.java @@ -1,4 +1,4 @@ -package org.apereo.openequella.integration.blackboard.webservice; +package com.tle.blackboard.webservice; import blackboard.platform.ws.anns.AuthenticatedMethod; diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/Folder.java b/oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/Folder.java similarity index 92% rename from oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/Folder.java rename to oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/Folder.java index 8019507..a8bb81f 100644 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/Folder.java +++ b/oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/Folder.java @@ -1,4 +1,4 @@ -package org.apereo.openequella.integration.blackboard.webservice; +package com.tle.blackboard.webservice; public class Folder extends Base { // private Course course; diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/SearchResult.java b/oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/SearchResult.java similarity index 91% rename from oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/SearchResult.java rename to oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/SearchResult.java index 1aa9330..0e84dde 100644 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/SearchResult.java +++ b/oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/SearchResult.java @@ -1,4 +1,4 @@ -package org.apereo.openequella.integration.blackboard.webservice; +package com.tle.blackboard.webservice; public class SearchResult { private int available; diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/BbWsSession.java b/oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/impl/BbWsSession.java similarity index 98% rename from oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/BbWsSession.java rename to oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/impl/BbWsSession.java index 4118809..b5dd2fb 100644 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/BbWsSession.java +++ b/oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/impl/BbWsSession.java @@ -1,4 +1,7 @@ -package org.apereo.openequella.integration.blackboard.webservice.impl; +package com.tle.blackboard.webservice.impl; + +import org.apereo.openequella.integration.blackboard.common.BbContext; +import org.apereo.openequella.integration.blackboard.common.BbUtil; import blackboard.data.ValidationException; import blackboard.data.content.Content; @@ -15,9 +18,6 @@ import blackboard.persist.user.UserDbLoader; import blackboard.platform.ws.SessionVO; -import org.apereo.openequella.integration.blackboard.common.BbContext; -import org.apereo.openequella.integration.blackboard.common.BbUtil; - /** * Container for various webservice session info * diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/PermissionException.java b/oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/impl/PermissionException.java similarity index 70% rename from oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/PermissionException.java rename to oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/impl/PermissionException.java index 35f38de..33782fd 100644 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/PermissionException.java +++ b/oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/impl/PermissionException.java @@ -1,4 +1,4 @@ -package org.apereo.openequella.integration.blackboard.webservice.impl; +package com.tle.blackboard.webservice.impl; public class PermissionException extends Exception { private static final long serialVersionUID = 1L; diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/SynchroniseContentThread.java b/oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/impl/SynchroniseContentThread.java similarity index 99% rename from oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/SynchroniseContentThread.java rename to oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/impl/SynchroniseContentThread.java index 5d35704..0b3b035 100644 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/SynchroniseContentThread.java +++ b/oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/impl/SynchroniseContentThread.java @@ -1,15 +1,10 @@ -package org.apereo.openequella.integration.blackboard.webservice.impl; +package com.tle.blackboard.webservice.impl; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; -import blackboard.data.content.Content; -import blackboard.persist.BbPersistenceManager; -import blackboard.persist.Id; -import blackboard.persist.content.ContentDbLoader; - import org.apereo.openequella.integration.blackboard.common.BbContext; import org.apereo.openequella.integration.blackboard.common.BbLogger; import org.apereo.openequella.integration.blackboard.common.BbUtil; @@ -19,6 +14,11 @@ import org.apereo.openequella.integration.blackboard.common.content.ItemKey; import org.apereo.openequella.integration.blackboard.common.content.RegistrationUtil; +import blackboard.data.content.Content; +import blackboard.persist.BbPersistenceManager; +import blackboard.persist.Id; +import blackboard.persist.content.ContentDbLoader; + /** * @author Aaron */ diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/WebServiceUtil.java b/oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/impl/WebServiceUtil.java similarity index 89% rename from oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/WebServiceUtil.java rename to oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/impl/WebServiceUtil.java index 6918667..0ddbd0c 100644 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/WebServiceUtil.java +++ b/oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/impl/WebServiceUtil.java @@ -1,4 +1,4 @@ -package org.apereo.openequella.integration.blackboard.webservice.impl; +package com.tle.blackboard.webservice.impl; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; @@ -14,6 +14,17 @@ import java.util.List; import java.util.Map; +import com.google.common.base.Strings; +import com.tle.blackboard.webservice.Course; +import com.tle.blackboard.webservice.Folder; +import com.tle.blackboard.webservice.SearchResult; + +import org.apereo.openequella.integration.blackboard.common.BbContext; +import org.apereo.openequella.integration.blackboard.common.BbUtil; +import org.apereo.openequella.integration.blackboard.common.content.ItemInfo; +import org.apereo.openequella.integration.blackboard.common.content.ItemKey; +import org.apereo.openequella.integration.blackboard.common.content.RegistrationUtil; + import blackboard.base.FormattedText; import blackboard.data.AttributePermission; import blackboard.data.ExtendedData; @@ -28,16 +39,6 @@ import blackboard.platform.ws.AxisHelpers; import blackboard.platform.ws.WebserviceLogger; -import com.google.common.base.Strings; -import org.apereo.openequella.integration.blackboard.common.BbContext; -import org.apereo.openequella.integration.blackboard.common.BbUtil; -import org.apereo.openequella.integration.blackboard.common.content.ItemInfo; -import org.apereo.openequella.integration.blackboard.common.content.ItemKey; -import org.apereo.openequella.integration.blackboard.common.content.RegistrationUtil; -import org.apereo.openequella.integration.blackboard.webservice.Course; -import org.apereo.openequella.integration.blackboard.webservice.Folder; -import org.apereo.openequella.integration.blackboard.webservice.SearchResult; - /** * This class was created SOLELY to reduce the size of EquellaWebserviceImpl * (Blackboard's bug @@ -175,10 +176,10 @@ static Iterable getBbCourses(Id userId) { } } - static org.apereo.openequella.integration.blackboard.webservice.Content convertUsage(ItemInfo usage, Course course, + static com.tle.blackboard.webservice.Content convertUsage(ItemInfo usage, Course course, Folder folder) { final ItemKey key = usage.getItemKey(); - final org.apereo.openequella.integration.blackboard.webservice.Content content = new org.apereo.openequella.integration.blackboard.webservice.Content(); + final com.tle.blackboard.webservice.Content content = new com.tle.blackboard.webservice.Content(); content.setCourseId(course.getId()); content.setFolderId(folder.getId()); content.setId(key.getContentId()); @@ -257,21 +258,21 @@ static void modifyContent(BbWsSession session, Content content, String courseId, } static SearchResult result(int available, - Collection contents, - Map courseMap, - Map folderMap) { + Collection contents, + Map courseMap, + Map folderMap) { final SearchResult result = new SearchResult(); result.setAvailable(available); result.setResults( - contents.toArray(new org.apereo.openequella.integration.blackboard.webservice.Content[contents.size()])); + contents.toArray(new com.tle.blackboard.webservice.Content[contents.size()])); final Collection courseCol = courseMap.values(); result.setCourses( - courseCol.toArray(new org.apereo.openequella.integration.blackboard.webservice.Course[courseCol.size()])); + courseCol.toArray(new com.tle.blackboard.webservice.Course[courseCol.size()])); final Collection folderCol = folderMap.values(); result.setFolders( - folderCol.toArray(new org.apereo.openequella.integration.blackboard.webservice.Folder[folderCol.size()])); + folderCol.toArray(new com.tle.blackboard.webservice.Folder[folderCol.size()])); return result; } diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/WsHelper.java b/oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/impl/WsHelper.java similarity index 90% rename from oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/WsHelper.java rename to oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/impl/WsHelper.java index c768370..0365753 100644 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/WsHelper.java +++ b/oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/impl/WsHelper.java @@ -1,12 +1,12 @@ -package org.apereo.openequella.integration.blackboard.webservice.impl; +package com.tle.blackboard.webservice.impl; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Map; import java.util.Set; -import org.apereo.openequella.integration.blackboard.webservice.Course; -import org.apereo.openequella.integration.blackboard.webservice.Folder; +import com.tle.blackboard.webservice.Course; +import com.tle.blackboard.webservice.Folder; import blackboard.data.content.Content; import blackboard.persist.Id; diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/ZEquellaWebserviceImpl.java b/oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/impl/ZEquellaWebserviceImpl.java similarity index 94% rename from oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/ZEquellaWebserviceImpl.java rename to oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/impl/ZEquellaWebserviceImpl.java index 4d71ce9..65bddec 100644 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/webservice/impl/ZEquellaWebserviceImpl.java +++ b/oeqPrimaryWS/src/main/java/com/tle/blackboard/webservice/impl/ZEquellaWebserviceImpl.java @@ -1,4 +1,4 @@ -package org.apereo.openequella.integration.blackboard.webservice.impl; +package com.tle.blackboard.webservice.impl; import java.util.ArrayList; import java.util.HashMap; @@ -12,6 +12,13 @@ import javax.jws.WebService; import javax.jws.soap.SOAPBinding; +import com.tle.blackboard.webservice.AddItemResult; +import com.tle.blackboard.webservice.Base; +import com.tle.blackboard.webservice.Course; +import com.tle.blackboard.webservice.EquellaWebservice; +import com.tle.blackboard.webservice.Folder; +import com.tle.blackboard.webservice.SearchResult; + import org.apereo.openequella.integration.blackboard.common.BbUtil; import org.apereo.openequella.integration.blackboard.common.content.ContentUtil; import org.apereo.openequella.integration.blackboard.common.content.ItemInfo; @@ -20,12 +27,6 @@ import org.apereo.openequella.integration.blackboard.common.content.LegacyItemUtil; import org.apereo.openequella.integration.blackboard.common.content.RegistrationUtil; import org.apereo.openequella.integration.blackboard.common.propbag.PropBagMin; -import org.apereo.openequella.integration.blackboard.webservice.AddItemResult; -import org.apereo.openequella.integration.blackboard.webservice.Base; -import org.apereo.openequella.integration.blackboard.webservice.Course; -import org.apereo.openequella.integration.blackboard.webservice.EquellaWebservice; -import org.apereo.openequella.integration.blackboard.webservice.Folder; -import org.apereo.openequella.integration.blackboard.webservice.SearchResult; import blackboard.data.ExtendedData; import blackboard.data.content.Content; @@ -46,7 +47,7 @@ * @author Aaron */ @SuppressWarnings("nls") -@WebService(name = "EQUELLA", serviceName = "EQUELLA", portName = "WS", targetNamespace = "http://webservice.blackboard.integration.openequella.apereo.org") +@WebService(name = "EQUELLA", serviceName = "EQUELLA", portName = "WS", targetNamespace = "http://webservice.blackboard.tle.com") @SOAPBinding(style = SOAPBinding.Style.DOCUMENT, use = SOAPBinding.Use.LITERAL, parameterStyle = SOAPBinding.ParameterStyle.WRAPPED) public class ZEquellaWebserviceImpl implements EquellaWebservice { private static final int CURRENT_EQUELLA_WS_VERSION = 3; @@ -239,7 +240,7 @@ public SearchResult findUsages(@WebParam(mode = WebParam.Mode.IN, name = "server final Map courseMap = new HashMap(); final Map folderMap = new HashMap(); - final List contents = new ArrayList(); + final List contents = new ArrayList(); for (ItemInfo usage : usages) { final ItemKey key = usage.getItemKey(); // if the course or folder no longer exist then forget it, the @@ -261,7 +262,7 @@ public SearchResult findUsages(@WebParam(mode = WebParam.Mode.IN, name = "server continue; } - final org.apereo.openequella.integration.blackboard.webservice.Content content = WebServiceUtil + final com.tle.blackboard.webservice.Content content = WebServiceUtil .convertUsage(usage, course, folder); // WebServiceUtil.debug("Adding content \"" + content + // "\" to return values"); @@ -303,7 +304,7 @@ public SearchResult findAllUsages(@WebParam(mode = WebParam.Mode.IN, name = "ser final Map courseMap = new HashMap(); final Map folderMap = new HashMap(); - final List contents = new ArrayList(); + final List contents = new ArrayList(); int index = 0; for (ItemInfo usage : usages) { final ItemKey key = usage.getItemKey(); @@ -321,7 +322,7 @@ public SearchResult findAllUsages(@WebParam(mode = WebParam.Mode.IN, name = "ser } if (index >= offset && (count < 0 || index < offset + count)) { - final org.apereo.openequella.integration.blackboard.webservice.Content content = WebServiceUtil + final com.tle.blackboard.webservice.Content content = WebServiceUtil .convertUsage(usage, course, folder); // WebServiceUtil.debug("Adding content \"" + content + // "\" to return values"); From 854af3e0e4aa272b4ec4a0f27a6032aceecee603 Mon Sep 17 00:00:00 2001 From: Aaron Holland Date: Sun, 17 Feb 2019 17:38:39 +1100 Subject: [PATCH 08/14] 4 fix issues with Postgres SQL --- .../blackboard/common/SqlUtil.java | 237 +++++-- .../common/content/RegistrationUtil.java | 644 ++++++++++++------ .../common/content/RegistrationUtil.java | 8 +- 3 files changed, 593 insertions(+), 296 deletions(-) diff --git a/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/SqlUtil.java b/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/SqlUtil.java index cdd40e0..c9ab0e8 100644 --- a/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/SqlUtil.java +++ b/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/SqlUtil.java @@ -16,17 +16,67 @@ */ @SuppressWarnings("nls") // @NonNullByDefault -public class SqlUtil { +public class SqlUtil +{ + public enum DbType + { + UNKNOWN, SQL_SERVER, ORACLE, POSTGRES + } + + private static DbType type = DbType.UNKNOWN; + private static final String TABLE = "equellacontent"; - public static StringBuilder select(String... columns) { + public static DbType getDatabaseType() + { + if( type == DbType.UNKNOWN ) + { + try + { + // Postgres test + runSql("SELECT current_database()", null); + type = DbType.POSTGRES; + } + catch( Exception e ) + { + try + { + // Oracle test + runSql("SELECT 1 FROM DUAL", null); + type = DbType.ORACLE; + } + catch( Exception e2 ) + { + try + { + // SQL server test + runSql("SELECT owner, table_name FROM all_tables", null); + type = DbType.SQL_SERVER; + } + catch( Exception e3 ) + { + type = DbType.UNKNOWN; + } + } + } + } + return type; + } + + public static StringBuilder select(String... columns) + { final StringBuilder sql = new StringBuilder("SELECT "); - if (columns.length == 0) { + if( columns.length == 0 ) + { sql.append("*"); - } else { + } + else + { boolean first = true; - for (String col : columns) { - if (!first) { + for( String col : columns ) + { + if( !first ) + { sql.append(", "); } sql.append(col); @@ -36,11 +86,14 @@ public static StringBuilder select(String... columns) { return sql.append(" ").append(from()); } - public static StringBuilder update(String... columns) { + public static StringBuilder update(String... columns) + { final StringBuilder sql = new StringBuilder("UPDATE ").append(table()).append("SET "); boolean first = true; - for (String col : columns) { - if (!first) { + for( String col : columns ) + { + if( !first ) + { sql.append(", "); } sql.append(col); @@ -50,11 +103,14 @@ public static StringBuilder update(String... columns) { return sql.append(" "); } - public static StringBuilder insert(String... columns) { + public static StringBuilder insert(String... columns) + { StringBuilder sql = new StringBuilder("INSERT INTO ").append(table()).append("("); boolean first = true; - for (String col : columns) { - if (!first) { + for( String col : columns ) + { + if( !first ) + { sql.append(", "); } sql.append(col); @@ -63,130 +119,171 @@ public static StringBuilder insert(String... columns) { return sql.append(") "); } - public static boolean columnExists(String column) { - try { - boolean oracle = DbUtil.safeGetBbDatabase().isOracle(); - SqlUtil.runSql("SELECT " + (oracle ? "" : "TOP 1 ") + " " + column + " FROM " + table(oracle) - + (oracle ? " WHERE rownum < 1" : ""), null); + public static boolean columnExists(String column) + { + try + { + final DbType t = getDatabaseType(); + SqlUtil.runSql("SELECT " + (t == DbType.SQL_SERVER ? "TOP 1 " : "") + " " + column + " FROM " + table() + + (t == DbType.ORACLE ? " WHERE rownum < 1" : (t == DbType.POSTGRES ? " LIMIT 1" : "")), null); return true; - } catch (Exception e) { + } + catch( Exception e ) + { // debug("Failed reading column "+ column +" from DB"); return false; } } - public static String delete() { + public static String delete() + { return "DELETE " + from(); } - public static String from() { + public static String from() + { return "FROM " + table(); } - public static String table(boolean oracle) { - return getSchema(oracle) + TABLE + " "; + public static String table() + { + return schema() + TABLE + " "; } - public static String table() { - return getSchema() + TABLE + " "; + public static String schema() + { + final DbType t = getDatabaseType(); + return (t != DbType.SQL_SERVER ? "" : "dbo."); } - public static String getSchema() { - return getSchema(DbUtil.safeGetBbDatabase().isOracle()); - } - - public static String getSchema(boolean oracle) { - return (oracle ? "" : "dbo."); - } - - public static List runSql(String sql, /* @Nullable */ResultProcessor processor, Object... params) { + public static List runSql(String sql, /* @Nullable */ResultProcessor processor, Object... params) + { PreparedStatement stmt = null; List result = null; - BbLogger.instance().logSqlTrace("Getting connection manager"); + //BbUtil.sqlTrace("Getting connection manager"); ConnectionManager connMgr = DbUtil.safeGetBbDatabase().getConnectionManager(); Connection conn = null; - try { - BbLogger.instance().logSqlTrace("Getting connection"); + try + { + //BbUtil.sqlTrace("Getting connection"); conn = connMgr.getConnection(); - BbLogger.instance().logSqlTrace("Creating statement"); + //BbUtil.sqlTrace("Creating statement"); stmt = conn.prepareStatement(sql); - BbLogger.instance().logSqlTrace("Has " + params.length + " params"); + //BbUtil.sqlTrace("Has " + params.length + " params"); int index = 1; - for (Object param : params) { - if (param instanceof OptionalParam) { + for( Object param : params ) + { + if( param instanceof OptionalParam ) + { final OptionalParam opt = (OptionalParam) param; - if (opt.isUsed()) { + if( opt.isUsed() ) + { setParam(stmt, index++, opt.getValue()); } - } else { + } + else + { setParam(stmt, index++, param); } } - BbLogger.instance().logSqlTrace("Executing: " + sql); - if (processor != null) { + //BbUtil.sqlTrace("Executing: " + sql); + if( processor != null ) + { result = processor.getResults(stmt.executeQuery()); - } else { + } + else + { stmt.execute(); result = (List) Collections.singletonList(stmt.getUpdateCount()); } - BbLogger.instance().logSqlTrace("Success!!"); + //BbUtil.sqlTrace("Success!!"); return result; - } catch (Exception e) { - BbLogger.instance().logError("Failed to runSql", e); - throw new RuntimeException(e); - } finally { - BbLogger.instance().logSqlTrace("Closing statement"); + } + catch( Throwable t ) + { + //BbUtil.error("Failed to runSql", t); + throw new RuntimeException(t); + } + finally + { + //BbUtil.sqlTrace("Closing statement"); DbUtil.closeStatement(stmt); - BbLogger.instance().logSqlTrace("Releasing connection"); + //BbUtil.sqlTrace("Releasing connection"); ConnectionManager.releaseDefaultConnection(conn); } } - public static void setParam(PreparedStatement stmt, int index, /* @Nullable */Object param) throws SQLException { - if (param instanceof String) { - BbLogger.instance().logSqlTrace("Setting param string[" + index + "] = " + param); + public static void setParam(PreparedStatement stmt, int index, /* @Nullable */Object param) throws SQLException + { + if( param instanceof String ) + { + //BbUtil.sqlTrace("Setting param string[" + index + "] = " + param); stmt.setString(index, (String) param); - } else if (param instanceof Integer) { - BbLogger.instance().logSqlTrace("Setting param int[" + index + "] = " + param); + } + else if( param instanceof Integer ) + { + //BbUtil.sqlTrace("Setting param int[" + index + "] = " + param); stmt.setInt(index, (Integer) param); - } else if (param instanceof Timestamp) { - BbLogger.instance().logSqlTrace("Setting param timestamp[" + index + "] = " + param); + } + else if( param instanceof Timestamp ) + { + //BbUtil.sqlTrace("Setting param timestamp[" + index + "] = " + param); stmt.setTimestamp(index, (Timestamp) param); - } else if (param instanceof Boolean) { + } + else if( param instanceof Boolean ) + { boolean pval = (Boolean) param; - BbLogger.instance().logSqlTrace("Setting param boolean[" + index + "] = " + pval); - stmt.setInt(index, pval ? 1 : 0); - } else if (param == null) { - BbLogger.instance().logSqlTrace("Setting param ?[" + index + "] = null"); + //BbUtil.sqlTrace("Setting param boolean[" + index + "] = " + pval); + final SqlUtil.DbType t = SqlUtil.getDatabaseType(); + if (t == DbType.POSTGRES) + { + // TODO: this should work on all types? + stmt.setBoolean(index, pval); + } + else + { + stmt.setInt(index, pval ? 1 : 0); + } + } + else if( param == null ) + { + //BbUtil.sqlTrace("Setting param ?[" + index + "] = null"); stmt.setString(index, null); - } else { + } + else + { throw new RuntimeException("Parameter " + index + " is an unhandled type: " + param.getClass().getName()); } } - public interface ResultProcessor { + public interface ResultProcessor + { List getResults(ResultSet results) throws SQLException; } - public static class OptionalParam { + public static class OptionalParam + { private final T value; private final boolean used; - public OptionalParam(T value, boolean used) { + public OptionalParam(T value, boolean used) + { this.value = value; this.used = used; } - public T getValue() { + public T getValue() + { return value; } - public boolean isUsed() { + public boolean isUsed() + { return used; } } diff --git a/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/content/RegistrationUtil.java b/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/content/RegistrationUtil.java index fa20069..0da1cba 100644 --- a/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/content/RegistrationUtil.java +++ b/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/content/RegistrationUtil.java @@ -12,14 +12,8 @@ import java.util.Date; import java.util.List; -import blackboard.db.BbDatabase; -import blackboard.persist.DatabaseContainer; -import blackboard.platform.persistence.PersistenceServiceFactory; - import com.google.common.base.Strings; -import org.apereo.openequella.integration.blackboard.common.BbLogger; -import org.apereo.openequella.integration.blackboard.common.BbUtil; import org.apereo.openequella.integration.blackboard.common.SqlUtil; import org.apereo.openequella.integration.blackboard.common.SqlUtil.OptionalParam; import org.apereo.openequella.integration.blackboard.common.SqlUtil.ResultProcessor; @@ -30,9 +24,9 @@ */ @SuppressWarnings("nls") // @NonNullByDefault -public abstract class RegistrationUtil { - // until proven otherwise. The databaseVersion is currently 5 (as of after 6.3 - // GA) +public abstract class RegistrationUtil +{ + // until proven otherwise. The databaseVersion is currently 5 (as of after 6.3 GA) private static int databaseVersion = 0; private static final String COL_DATABASE_ID = "ID"; @@ -53,117 +47,146 @@ public abstract class RegistrationUtil { private static final String COL_DATE_ACCESSED = "DTACCESSED"; private static final String COL_ATTACHMENT_NAME = "ATTNAME"; - public RegistrationUtil() { + public RegistrationUtil() + { throw new Error(); } public static void recordItem(ItemKey itemKey, boolean added, boolean available, boolean courseAvailable, - String bbTitle, String bbDesc, String attachmentName, Date dateAdded, Date dateModifed, String courseName) { - BbLogger.instance() - .logTrace("recordItem(" + itemKey + ", " + added + ", " + available + ", " + courseAvailable + ", " + bbTitle - + ", " + bbDesc + ", " + attachmentName + ", " + dateAdded + ", " + dateModifed + ", " + courseName + ")"); + String bbTitle, String bbDesc, String attachmentName, Date dateAdded, Date dateModifed, String courseName) + { + //BbUtil.trace("recordItem(" + itemKey + ", " + added + ", " + available + ", " + courseAvailable + ", " + bbTitle + // + ", " + bbDesc + ", " + attachmentName + ", " + dateAdded + ", " + dateModifed + ", " + courseName + ")"); ensureDatabase(); StringBuilder sql = null; - final boolean oracle = getDatabase().isOracle(); - final String schema = SqlUtil.getSchema(oracle); + final SqlUtil.DbType t = SqlUtil.getDatabaseType(); + final String schema = SqlUtil.schema(); final long now = System.currentTimeMillis(); final java.sql.Timestamp addedTimestamp = new java.sql.Timestamp(dateAdded == null ? now : dateAdded.getTime()); final java.sql.Timestamp modifiedTimestamp = new java.sql.Timestamp( - dateModifed == null ? now : dateModifed.getTime()); - - if (added) { - if (oracle) { - sql = insert(COL_DATABASE_ID, COL_DATE_CREATED, COL_DATE_MODIFIED, COL_INSTITUTION_URL, COL_UUID, COL_VERSION, - COL_COURSE_ID, COL_FOLDER_ID, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, - COL_BLACKBOARD_DESC, COL_PAGE, COL_CONTENT_ID, COL_COURSE_NAME, COL_ATTACHMENT_NAME).append(" VALUES (") - .append(schema).append("equellaseq.nextval, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ,?, ?, ?, ?)"); - } else { - sql = insert(COL_DATE_CREATED, COL_DATE_MODIFIED, COL_INSTITUTION_URL, COL_UUID, COL_VERSION, COL_COURSE_ID, - COL_FOLDER_ID, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC, COL_PAGE, - COL_CONTENT_ID, COL_COURSE_NAME, COL_ATTACHMENT_NAME) - .append(" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + dateModifed == null ? now : dateModifed.getTime()); + + if( added ) + { + if( t == SqlUtil.DbType.ORACLE ) + { + sql = insert(COL_DATABASE_ID, COL_DATE_CREATED, COL_DATE_MODIFIED, COL_INSTITUTION_URL, COL_UUID, + COL_VERSION, COL_COURSE_ID, COL_FOLDER_ID, COL_AVAILABLE, COL_COURSE_AVAILABLE, + COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC, COL_PAGE, COL_CONTENT_ID, COL_COURSE_NAME, + COL_ATTACHMENT_NAME).append(" VALUES (").append(schema) + .append("equellaseq.nextval, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ,?, ?, ?, ?)"); + } + else if( t == SqlUtil.DbType.SQL_SERVER ) + { + sql = insert(COL_DATE_CREATED, COL_DATE_MODIFIED, COL_INSTITUTION_URL, COL_UUID, COL_VERSION, + COL_COURSE_ID, COL_FOLDER_ID, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, + COL_BLACKBOARD_DESC, COL_PAGE, COL_CONTENT_ID, COL_COURSE_NAME, COL_ATTACHMENT_NAME) + .append(" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); } + else if( t == SqlUtil.DbType.POSTGRES ) + { + sql = insert(COL_DATABASE_ID, COL_DATE_CREATED, COL_DATE_MODIFIED, COL_INSTITUTION_URL, COL_UUID, + COL_VERSION, COL_COURSE_ID, COL_FOLDER_ID, COL_AVAILABLE, COL_COURSE_AVAILABLE, + COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC, COL_PAGE, COL_CONTENT_ID, COL_COURSE_NAME, + COL_ATTACHMENT_NAME) + .append(" VALUES (nextval('equellaseq'), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + } + SqlUtil.runSql(sql.toString(), null, addedTimestamp, modifiedTimestamp, itemKey.getInstitutionUrl(), - itemKey.getUuid(), itemKey.getVersion(), itemKey.getCourseId(), itemKey.getFolderId(), available, - courseAvailable, bbTitle, trim(bbDesc, 511), itemKey.getPage(), itemKey.getContentId(), courseName, - attachmentName); + itemKey.getUuid(), itemKey.getVersion(), itemKey.getCourseId(), itemKey.getFolderId(), available, + courseAvailable, bbTitle, trim(bbDesc, 511), itemKey.getPage(), itemKey.getContentId(), courseName, + attachmentName); itemKey.setDatabaseId(0); // would be nice to know what the new one - // is - } else { + // is + } + else + { String contentId = itemKey.getContentId(); - if (!Strings.isNullOrEmpty(contentId)) { - BbLogger.instance().logDebug("updating recorded usage using contentId"); + if( !Strings.isNullOrEmpty(contentId) ) + { + //BbUtil.debug("updating recorded usage using contentId"); - sql = update(COL_DATE_MODIFIED, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC, - COL_PAGE, COL_COURSE_NAME, COL_ATTACHMENT_NAME).append("WHERE CONTENTID=?"); - List updateCount = SqlUtil.runSql(sql.toString(), null, modifiedTimestamp, available, courseAvailable, - bbTitle, bbDesc, itemKey.getPage(), courseName, attachmentName, contentId); + sql = update(COL_DATE_MODIFIED, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, + COL_BLACKBOARD_DESC, COL_PAGE, COL_COURSE_NAME, COL_ATTACHMENT_NAME).append("WHERE CONTENTID=?"); + List updateCount = SqlUtil.runSql(sql.toString(), null, modifiedTimestamp, available, + courseAvailable, bbTitle, bbDesc, itemKey.getPage(), courseName, attachmentName, contentId); int updates = updateCount.get(0); - if (updates > 1) { + if( updates > 1 ) + { // There have instances of dupes. Remove them all. This // should be a rare occurrence - BbLogger.instance().logError("** duplicate recordings of " + contentId + " **", null); + //BbUtil.error("** duplicate recordings of " + contentId + " **", null); // remove dupes and add a brand new entry unrecordItem(0, contentId); - recordItem(itemKey, true, available, courseAvailable, bbTitle, bbDesc, attachmentName, dateAdded, dateModifed, - courseName); - } else if (updates == 0) { - BbLogger.instance().logDebug("content ID not logged... updating the existing one and record the content ID"); + recordItem(itemKey, true, available, courseAvailable, bbTitle, bbDesc, attachmentName, dateAdded, + dateModifed, courseName); + } + else if( updates == 0 ) + { + //BbUtil.debug("content ID not logged... updating the existing one and record the content ID"); sql = update(COL_DATE_MODIFIED, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, - COL_BLACKBOARD_DESC, COL_PAGE, COL_COURSE_NAME, COL_CONTENT_ID, COL_ATTACHMENT_NAME) - .append("WHERE INSTURL=? AND UUID=? AND VERSION=? AND COURSEID=? AND FOLDERID=?"); - updateCount = SqlUtil.runSql(sql.toString(), null, modifiedTimestamp, available, courseAvailable, bbTitle, - trim(bbDesc, 511), itemKey.getPage(), courseName, itemKey.getContentId(), attachmentName, - itemKey.getInstitutionUrl(), itemKey.getUuid(), itemKey.getVersion(), itemKey.getCourseId(), - itemKey.getFolderId()); + COL_BLACKBOARD_DESC, COL_PAGE, COL_COURSE_NAME, COL_CONTENT_ID, COL_ATTACHMENT_NAME) + .append("WHERE INSTURL=? AND UUID=? AND VERSION=? AND COURSEID=? AND FOLDERID=?"); + updateCount = SqlUtil.runSql(sql.toString(), null, modifiedTimestamp, available, courseAvailable, + bbTitle, trim(bbDesc, 511), itemKey.getPage(), courseName, itemKey.getContentId(), + attachmentName, itemKey.getInstitutionUrl(), itemKey.getUuid(), itemKey.getVersion(), + itemKey.getCourseId(), itemKey.getFolderId()); checkSingleUpdate(updateCount); } - } else { - BbLogger.instance().logDebug("updating recorded usage using old field matching"); + } + else + { + //BbUtil.debug("updating recorded usage using old field matching"); - sql = update(COL_DATE_MODIFIED, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC, - COL_PAGE, COL_COURSE_NAME, COL_ATTACHMENT_NAME) - .append("WHERE INSTURL=? AND UUID=? AND VERSION=? AND COURSEID=? AND FOLDERID=?"); + sql = update(COL_DATE_MODIFIED, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, + COL_BLACKBOARD_DESC, COL_PAGE, COL_COURSE_NAME, COL_ATTACHMENT_NAME) + .append("WHERE INSTURL=? AND UUID=? AND VERSION=? AND COURSEID=? AND FOLDERID=?"); final List updateCount = SqlUtil.runSql(sql.toString(), null, modifiedTimestamp, available, - courseAvailable, bbTitle, trim(bbDesc, 511), itemKey.getPage(), courseName, attachmentName, - itemKey.getInstitutionUrl(), itemKey.getUuid(), itemKey.getVersion(), itemKey.getCourseId(), - itemKey.getFolderId()); + courseAvailable, bbTitle, trim(bbDesc, 511), itemKey.getPage(), courseName, attachmentName, + itemKey.getInstitutionUrl(), itemKey.getUuid(), itemKey.getVersion(), itemKey.getCourseId(), + itemKey.getFolderId()); checkSingleUpdate(updateCount); } } } - private static boolean checkSingleUpdate(List updateCount) { + private static boolean checkSingleUpdate(List updateCount) + { final int updates = updateCount.get(0); - if (updates != 1) { + if( updates != 1 ) + { // houston, we have a problem - BbLogger.instance().logError("** Updated " + updates + " rows when only one should have been updated **", null); + //BbUtil.error("** Updated " + updates + " rows when only one should have been updated **", null); return false; } return true; } - private static boolean unrecordItem(int usageId) { - BbLogger.instance().logDebug("unrecordItem(" + usageId + ")"); + private static boolean unrecordItem(int usageId) + { + //BbUtil.debug("unrecordItem(" + usageId + ")"); ensureDatabase(); final List updateCount = SqlUtil.runSql(delete() + "WHERE ID=?", null, usageId); return updateCount.get(0) > 0; } - public static boolean unrecordItem(int usageId, String contentId) { - BbLogger.instance().logDebug("unrecordItem(" + usageId + ", " + contentId + ")"); + public static boolean unrecordItem(int usageId, String contentId) + { + //BbUtil.debug("unrecordItem(" + usageId + ", " + contentId + ")"); ensureDatabase(); - if (usageId != 0) { + if( usageId != 0 ) + { boolean unrecorded = unrecordItem(usageId); - if (unrecorded) { + if( unrecorded ) + { return true; } } @@ -172,63 +195,74 @@ public static boolean unrecordItem(int usageId, String contentId) { return updateCount.get(0) > 0; } - public static boolean updateRecordedTitle(String contentId, String title, String description) { - BbLogger.instance().logDebug("updateRecordedTitle(" + contentId + ", " + title + ", " + description + ")"); + public static boolean updateRecordedTitle(String contentId, String title, String description) + { + //BbUtil.debug("updateRecordedTitle(" + contentId + ", " + title + ", " + description + ")"); ensureDatabase(); final List updateCount = SqlUtil.runSql( - update(COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC).append("WHERE CONTENTID=?").toString(), null, title, - description, contentId); + update(COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC).append("WHERE CONTENTID=?").toString(), null, title, + description, contentId); return updateCount.get(0) > 0; } - public static boolean updateRecordedLocation(String contentId, String courseId, String folderId) { - BbLogger.instance().logDebug("updateRecordedLocation(" + contentId + ", " + courseId + ", " + folderId + ")"); + public static boolean updateRecordedLocation(String contentId, String courseId, String folderId) + { + //BbUtil.debug("updateRecordedLocation(" + contentId + ", " + courseId + ", " + folderId + ")"); ensureDatabase(); final List updateCount = SqlUtil.runSql( - update(COL_COURSE_ID, COL_FOLDER_ID).append("WHERE CONTENTID=?").toString(), null, courseId, folderId, - contentId); + update(COL_COURSE_ID, COL_FOLDER_ID).append("WHERE CONTENTID=?").toString(), null, courseId, folderId, + contentId); return updateCount.get(0) > 0; } - public static boolean updateDateAccessed(String contentId, Date dateAccessed) { - BbLogger.instance().logDebug("updateDateAccessed(" + contentId + ", " + dateAccessed + ")"); + public static boolean updateDateAccessed(String contentId, Date dateAccessed) + { + //BbUtil.debug("updateDateAccessed(" + contentId + ", " + dateAccessed + ")"); ensureDatabase(); final Timestamp accessedTimestamp = new Timestamp(dateAccessed.getTime()); - final List updateCount = SqlUtil.runSql(update(COL_DATE_ACCESSED).append("WHERE CONTENTID=?").toString(), - null, accessedTimestamp, contentId); + final List updateCount = SqlUtil.runSql( + update(COL_DATE_ACCESSED).append("WHERE CONTENTID=?").toString(), null, accessedTimestamp, contentId); return updateCount.get(0) > 0; } public static List findUsages(final String institutionUrl, final String itemUuid, final int itemVersion, - boolean versionIsLatest, boolean allVersions, boolean available) { - BbLogger.instance().logDebug("findUsages(" + institutionUrl + ", " + itemUuid + ", " + itemVersion + ", " - + allVersions + ", " + available + ")"); + boolean versionIsLatest, boolean allVersions, boolean available) + { + //BbUtil.debug("findUsages(" + institutionUrl + ", " + itemUuid + ", " + itemVersion + ", " + allVersions + ", " + // + available + ")"); ensureDatabase(); final StringBuilder sql = select().append("WHERE INSTURL=? AND UUID=?"); - if (!allVersions) { + if( !allVersions ) + { sql.append(" AND (VERSION=?"); - if (versionIsLatest) { + if( versionIsLatest ) + { sql.append(" OR VERSION=0"); } sql.append(")"); } - if (available) { + if( available ) + { sql.append(" AND AVAILABLE=? AND COURSEAVAILABLE=?"); } sql.append(" ORDER BY DTCREATED DESC"); - final List usages = SqlUtil.runSql(sql.toString(), new ResultProcessor() { + final List usages = SqlUtil.runSql(sql.toString(), new ResultProcessor() + { @Override - public List getResults(ResultSet results) throws SQLException { + public List getResults(ResultSet results) throws SQLException + { final List u = new ArrayList(); - while (results.next()) { + while( results.next() ) + { final String contentId = results.getString(COL_CONTENT_ID); final ItemInfo r = new ItemInfo(institutionUrl, itemUuid, results.getInt(COL_VERSION), contentId, - results.getString(COL_COURSE_ID), results.getString(COL_FOLDER_ID), results.getString(COL_PAGE)); + results.getString(COL_COURSE_ID), results.getString(COL_FOLDER_ID), + results.getString(COL_PAGE)); r.setCreatedDate(results.getTimestamp(COL_DATE_CREATED)); r.setModifiedDate(results.getTimestamp(COL_DATE_MODIFIED)); r.setAvailable(results.getBoolean(COL_AVAILABLE)); @@ -241,94 +275,123 @@ public List getResults(ResultSet results) throws SQLException { return u; } }, institutionUrl, itemUuid, new OptionalParam(itemVersion, !allVersions), - new OptionalParam(available, available), new OptionalParam(available, available)); + new OptionalParam(available, available), new OptionalParam(available, available)); return usages; } public static List findAllUsages(final String institutionUrl, final String query, final String courseId, - String folderId, boolean available, String sortColumn, boolean sortReverse) { - BbLogger.instance().logDebug("findAllUsages(" + institutionUrl + ", " + query + ", " + courseId + ", " + folderId - + ", " + available + ", " + sortColumn + ", " + sortReverse + ")"); + String folderId, boolean available, String sortColumn, boolean sortReverse) + { + //BbUtil.debug("findAllUsages(" + institutionUrl + ", " + query + ", " + courseId + ", " + folderId + ", " + // + available + ", " + sortColumn + ", " + sortReverse + ")"); ensureDatabase(); final StringBuilder sql = select().append(" WHERE INSTURL=?"); final boolean hasQuery = !Strings.isNullOrEmpty(query); String likeQuery = null; - if (hasQuery) { + if( hasQuery ) + { likeQuery = '%' + query.toLowerCase() + '%'; sql.append(" AND LOWER(BBTITLE) LIKE ?"); } final boolean hasCourseId = !Strings.isNullOrEmpty(courseId); - if (hasCourseId) { + if( hasCourseId ) + { sql.append(" AND COURSEID = ?"); } final boolean hasFolderId = !Strings.isNullOrEmpty(folderId); - if (hasFolderId) { + if( hasFolderId ) + { sql.append(" AND FOLDERID = ?"); } - if (available) { + if( available ) + { sql.append(" AND AVAILABLE=? AND COURSEAVAILABLE=?"); } String sortCol = "DTCREATED"; String sortOrd = sortReverse ? "DESC" : "ASC"; - if (!Strings.isNullOrEmpty(sortColumn)) { - if (sortColumn.equals("name")) { + if( !Strings.isNullOrEmpty(sortColumn) ) + { + if( sortColumn.equals("name") ) + { sortCol = "LOWER(BBTITLE)"; - } else if (sortColumn.equals("course")) { + } + else if( sortColumn.equals("course") ) + { sortCol = "LOWER(COURSENAME)"; } } sql.append(" ORDER BY " + sortCol + " " + sortOrd); - // @formatter:off - final List usages = SqlUtil.runSql(sql.toString(), new ResultProcessor() { - @Override - public List getResults(ResultSet results) throws SQLException { - final List u = new ArrayList(); - while (results.next()) { - final ItemInfo r = new ItemInfo(institutionUrl, results.getString(COL_UUID), results.getInt(COL_VERSION), - results.getString(COL_CONTENT_ID), results.getString(COL_COURSE_ID), results.getString(COL_FOLDER_ID), - results.getString(COL_PAGE)); - r.setCreatedDate(results.getTimestamp(COL_DATE_CREATED)); - r.setModifiedDate(results.getTimestamp(COL_DATE_MODIFIED)); - r.setAvailable(results.getBoolean(COL_AVAILABLE)); - r.setName(results.getString(COL_BLACKBOARD_TITLE)); - r.setDescription(results.getString(COL_BLACKBOARD_DESC)); - r.getItemKey().setDatabaseId(results.getInt(COL_DATABASE_ID)); - r.setDateAccessed(results.getTimestamp(COL_DATE_ACCESSED)); - r.setAttachmentName(results.getString(COL_ATTACHMENT_NAME)); - u.add(r); - } - return u; - } - }, institutionUrl, new OptionalParam(likeQuery, hasQuery), new OptionalParam(courseId, hasCourseId), - new OptionalParam(folderId, hasFolderId), new OptionalParam(available, available), - new OptionalParam(available, available)); - // @formatter:on + //@formatter:off + final List usages = SqlUtil.runSql( + sql.toString(), + new ResultProcessor() + { + @Override + public List getResults(ResultSet results) throws SQLException + { + final List u = new ArrayList(); + while( results.next() ) + { + final ItemInfo r = new ItemInfo( + institutionUrl, + results.getString(COL_UUID), + results.getInt(COL_VERSION), + results.getString(COL_CONTENT_ID), + results.getString(COL_COURSE_ID), + results.getString(COL_FOLDER_ID), + results.getString(COL_PAGE) + ); + r.setCreatedDate(results.getTimestamp(COL_DATE_CREATED)); + r.setModifiedDate(results.getTimestamp(COL_DATE_MODIFIED)); + r.setAvailable(results.getBoolean(COL_AVAILABLE)); + r.setName(results.getString(COL_BLACKBOARD_TITLE)); + r.setDescription(results.getString(COL_BLACKBOARD_DESC)); + r.getItemKey().setDatabaseId(results.getInt(COL_DATABASE_ID)); + r.setDateAccessed(results.getTimestamp(COL_DATE_ACCESSED)); + r.setAttachmentName(results.getString(COL_ATTACHMENT_NAME)); + u.add(r); + } + return u; + } + }, + institutionUrl, + new OptionalParam(likeQuery, hasQuery), + new OptionalParam(courseId, hasCourseId), + new OptionalParam(folderId, hasFolderId), + new OptionalParam(available, available), + new OptionalParam(available, available) + ); + //@formatter:on return usages; } // Used by SynchroniseContentThread public static List findEquellaContentByCourse(final String institutionUrl, final String courseId, - boolean available) { - BbLogger.instance() - .logDebug("findEquellaContentByCourse(" + institutionUrl + ", " + courseId + ", " + available + ")"); + boolean available) + { + //BbUtil.debug("findEquellaContentByCourse(" + institutionUrl + ", " + courseId + ", " + available + ")"); ensureDatabase(); // ID, UUID, VERSION, FOLDERID, ATTACHMENT_NAME, CONTENTID - final StringBuilder sql = select(COL_DATABASE_ID, COL_UUID, COL_VERSION, COL_FOLDER_ID, COL_PAGE, COL_CONTENT_ID) - .append("WHERE INSTURL=? AND COURSEID=?"); - if (available) { + final StringBuilder sql = select(COL_DATABASE_ID, COL_UUID, COL_VERSION, COL_FOLDER_ID, COL_PAGE, + COL_CONTENT_ID).append("WHERE INSTURL=? AND COURSEID=?"); + if( available ) + { sql.append(" AND AVAILABLE=? AND COURSEAVAILABLE=?"); } - final List usages = SqlUtil.runSql(sql.toString(), new ResultProcessor() { + final List usages = SqlUtil.runSql(sql.toString(), new ResultProcessor() + { @Override - public List getResults(ResultSet results) throws SQLException { + public List getResults(ResultSet results) throws SQLException + { List u = new ArrayList(); - while (results.next()) { + while( results.next() ) + { final int dbId = results.getInt(COL_DATABASE_ID); final String uuid = results.getString(COL_UUID); final int version = results.getInt(COL_VERSION); @@ -343,12 +406,13 @@ public List getResults(ResultSet results) throws SQLException { return u; } }, institutionUrl, courseId, new OptionalParam(available, available), - new OptionalParam(available, available)); + new OptionalParam(available, available)); return usages; } - public static int cleanupBadContent(final String institutionUrl) { - BbLogger.instance().logDebug("cleanupBadContent(" + institutionUrl + ")"); + public static int cleanupBadContent(final String institutionUrl) + { + //BbUtil.debug("cleanupBadContent(" + institutionUrl + ")"); ensureDatabase(); String sql = delete() + "WHERE INSTURL=? AND CONTENTID IS NULL"; @@ -356,17 +420,21 @@ public static int cleanupBadContent(final String institutionUrl) { return updates.get(0); } - public static ContentRegisteredResponse contentIsRegistered(ItemKey itemKey) { - BbLogger.instance().logDebug("contentIsRegistered(" + itemKey + ")"); + public static ContentRegisteredResponse contentIsRegistered(ItemKey itemKey) + { + //BbUtil.debug("contentIsRegistered(" + itemKey + ")"); ensureDatabase(); - final ResultProcessor resultProcessor = new ResultProcessor() { + final ResultProcessor resultProcessor = new ResultProcessor() + { @Override - public List getResults(ResultSet results) throws SQLException { + public List getResults(ResultSet results) throws SQLException + { List r = new ArrayList(); - while (results.next()) { - r.add(new Object[] { results.getInt(COL_DATABASE_ID), results.getBoolean(COL_AVAILABLE), - results.getString(COL_CONTENT_ID) }); + while( results.next() ) + { + r.add(new Object[]{results.getInt(COL_DATABASE_ID), results.getBoolean(COL_AVAILABLE), + results.getString(COL_CONTENT_ID)}); } return r; } @@ -374,22 +442,26 @@ public List getResults(ResultSet results) throws SQLException { final List res; final StringBuilder sql = select(COL_DATABASE_ID, COL_AVAILABLE, COL_CONTENT_ID); - if (itemKey.getContentId() != null) { + if( itemKey.getContentId() != null ) + { sql.append("WHERE CONTENTID=?"); res = SqlUtil.runSql(sql.toString(), resultProcessor, itemKey.getContentId()); - } else { + } + else + { // OLD STYLE final String page = itemKey.getPage(); sql.append("WHERE INSTURL=? AND UUID=? AND VERSION=? AND COURSEID=? AND FOLDERID=? AND PAGE" - + (Strings.isNullOrEmpty(page) ? " IS NULL" : "=?")); + + (Strings.isNullOrEmpty(page) ? " IS NULL" : "=?")); res = SqlUtil.runSql(sql.toString(), resultProcessor, itemKey.getInstitutionUrl(), itemKey.getUuid(), - itemKey.getVersion(), itemKey.getCourseId(), itemKey.getFolderId(), - new OptionalParam(page, !Strings.isNullOrEmpty(page))); + itemKey.getVersion(), itemKey.getCourseId(), itemKey.getFolderId(), + new OptionalParam(page, !Strings.isNullOrEmpty(page))); } final ContentRegisteredResponse response = new ContentRegisteredResponse(); - if (!res.isEmpty()) { + if( !res.isEmpty() ) + { response.setRegistered(true); final Object[] r = res.get(0); response.setId((Integer) r[0]); @@ -400,40 +472,53 @@ public List getResults(ResultSet results) throws SQLException { return response; } - private static void ensureDatabase() { - if (databaseVersion < 1) { - try { + private static void ensureDatabase() + { + if( databaseVersion < 1 ) + { + try + { SqlUtil.runSql(SqlUtil.select("1").toString(), null); databaseVersion = 1; - } catch (Exception e) { + } + catch( Exception e ) + { makeDatabase(); databaseVersion = 1; } } - if (databaseVersion < 2) { - if (!SqlUtil.columnExists(COL_CONTENT_ID)) { + if( databaseVersion < 2 ) + { + if( !SqlUtil.columnExists(COL_CONTENT_ID) ) + { modifyDatabaseAddContentAndCourseName(); } databaseVersion = 2; } - if (databaseVersion < 3) { - if (!SqlUtil.columnExists(COL_BLACKBOARD_DESC)) { + if( databaseVersion < 3 ) + { + if( !SqlUtil.columnExists(COL_BLACKBOARD_DESC) ) + { modifyDatabaseAddDescription(); } databaseVersion = 3; } - if (databaseVersion < 4) { - if (!SqlUtil.columnExists(COL_DATE_ACCESSED)) { + if( databaseVersion < 4 ) + { + if( !SqlUtil.columnExists(COL_DATE_ACCESSED) ) + { modifyDatabaseAddDateAccessed(); } databaseVersion = 4; } - if (databaseVersion < 5) { - if (!SqlUtil.columnExists(COL_ATTACHMENT_NAME)) { + if( databaseVersion < 5 ) + { + if( !SqlUtil.columnExists(COL_ATTACHMENT_NAME) ) + { modifyDatabaseAddAttachmentName(); } databaseVersion = 5; @@ -443,111 +528,220 @@ private static void ensureDatabase() { /** * Todo: this clearly isn't normalised. But the question is, is it worth it? */ - private static void makeDatabase() { - final String sql; - if (getDatabase().isOracle()) { - BbLogger.instance().logDebug("Oracle DB detected"); + private static void makeDatabase() + { + final SqlUtil.DbType t = SqlUtil.getDatabaseType(); + if( t == SqlUtil.DbType.ORACLE ) + { + //BbUtil.debug("Oracle DB detected"); SqlUtil.runSql("CREATE SEQUENCE equellaseq START WITH 1 INCREMENT BY 1 NOMAXVALUE", null); - // @formatter:off - sql = "CREATE TABLE equellacontent (" + "ID NUMBER NOT NULL, " + "INSTURL NVARCHAR2(255) NOT NULL, " - + "DTCREATED DATE DEFAULT SYSDATE, " + "DTMODIFIED DATE DEFAULT SYSDATE, " - + "BBTITLE NVARCHAR2(1024) NOT NULL," + "PAGE NVARCHAR2(1024)," // nullable for item summaries - + "UUID NVARCHAR2(40) NOT NULL, " + "VERSION NUMBER NOT NULL," + "COURSEID NVARCHAR2(32) NOT NULL, " - + "FOLDERID NVARCHAR2(32) NOT NULL, " + "AVAILABLE NUMBER NOT NULL," + "COURSEAVAILABLE NUMBER NOT NULL," - + "CONSTRAINT equellacontent_pk PRIMARY KEY (ID))"; - // @formatter:on + //@formatter:off + final String sql = "CREATE TABLE equellacontent (" + + "ID NUMBER NOT NULL, " + + "INSTURL NVARCHAR2(255) NOT NULL, " + + "DTCREATED DATE DEFAULT SYSDATE, " + + "DTMODIFIED DATE DEFAULT SYSDATE, " + + "BBTITLE NVARCHAR2(1024) NOT NULL," + + "PAGE NVARCHAR2(1024)," //nullable for item summaries + + "UUID NVARCHAR2(40) NOT NULL, " + + "VERSION NUMBER NOT NULL," + + "COURSEID NVARCHAR2(32) NOT NULL, " + + "FOLDERID NVARCHAR2(32) NOT NULL, " + + "AVAILABLE NUMBER NOT NULL," + + "COURSEAVAILABLE NUMBER NOT NULL," + + "CONSTRAINT equellacontent_pk PRIMARY KEY (ID))"; + //@formatter:on SqlUtil.runSql(sql, null); SqlUtil.runSql("CREATE INDEX eqcontent_uuidversion ON equellacontent(INSTURL, UUID, VERSION)", null); // cannot be UNIQUE since same item could be added to same folder // (in theory) - SqlUtil.runSql("CREATE INDEX eqcontent_all ON equellacontent(INSTURL, UUID, VERSION, COURSEID, FOLDERID)", null); + SqlUtil.runSql("CREATE INDEX eqcontent_all ON equellacontent(INSTURL, UUID, VERSION, COURSEID, FOLDERID)", + null); SqlUtil.runSql("CREATE INDEX eqcontent_course ON equellacontent(INSTURL, COURSEID)", null); SqlUtil.runSql("CREATE INDEX eqcontent_available ON equellacontent(AVAILABLE, COURSEAVAILABLE)", null); - } else { - BbLogger.instance().logDebug("SQL Server DB detected"); - // @formatter:off - sql = "CREATE TABLE dbo.equellacontent (" + "ID int NOT NULL PRIMARY KEY IDENTITY, " - + "INSTURL nvarchar(255) NOT NULL, " + "BBTITLE nvarchar(1024) NOT NULL, " + "PAGE nvarchar(1024) NULL, " // nullable - // for - // item - // summaries - + "DTCREATED datetime NOT NULL, " + "DTMODIFIED datetime NOT NULL, " + "UUID nvarchar(40) NOT NULL, " - + "VERSION int NOT NULL," + "COURSEID nvarchar(32) NOT NULL, " + "FOLDERID nvarchar(32) NOT NULL, " - + "AVAILABLE bit NOT NULL," + "COURSEAVAILABLE bit NOT NULL" + ")"; - // @formatter:on + } + else if( t == SqlUtil.DbType.SQL_SERVER ) + { + //BbUtil.debug("SQL Server DB detected"); + + //@formatter:off + final String sql = "CREATE TABLE dbo.equellacontent (" + + "ID int NOT NULL PRIMARY KEY IDENTITY, " + + "INSTURL nvarchar(255) NOT NULL, " + + "BBTITLE nvarchar(1024) NOT NULL, " + + "PAGE nvarchar(1024) NULL, " //nullable for item summaries + + "DTCREATED datetime NOT NULL, " + + "DTMODIFIED datetime NOT NULL, " + + "UUID nvarchar(40) NOT NULL, " + + "VERSION int NOT NULL," + + "COURSEID nvarchar(32) NOT NULL, " + + "FOLDERID nvarchar(32) NOT NULL, " + + "AVAILABLE bit NOT NULL," + + "COURSEAVAILABLE bit NOT NULL" + + ")"; + //@formatter:on SqlUtil.runSql(sql, null); SqlUtil.runSql("CREATE INDEX eqcontent_course ON dbo.equellacontent(INSTURL, COURSEID)", null); // cannot be UNIQUE since same item could be added to same folder // (in theory) - SqlUtil.runSql("CREATE INDEX eqcontent_all ON dbo.equellacontent(INSTURL, UUID, VERSION, COURSEID, FOLDERID)", - null); + SqlUtil.runSql( + "CREATE INDEX eqcontent_all ON dbo.equellacontent(INSTURL, UUID, VERSION, COURSEID, FOLDERID)", null); SqlUtil.runSql("CREATE INDEX eqcontent_uuidversion ON dbo.equellacontent(INSTURL, UUID, VERSION)", null); SqlUtil.runSql("CREATE INDEX eqcontent_available ON dbo.equellacontent(AVAILABLE, COURSEAVAILABLE)", null); } + else if( t == SqlUtil.DbType.POSTGRES ) + { + //BbUtil.debug("Postgres DB detected"); + + try + { + SqlUtil.runSql("CREATE SEQUENCE equellaseq START WITH 1 INCREMENT BY 1 NO MAXVALUE", null); + } + catch( Exception e ) + { + // Ignore, it may already exist + } + + //@formatter:off + final String sql = "CREATE TABLE equellacontent (" + + "ID int NOT NULL PRIMARY KEY, " + + "INSTURL varchar(255) NOT NULL, " + + "BBTITLE varchar(1024) NOT NULL, " + + "PAGE varchar(1024) NULL, " //nullable for item summaries + + "DTCREATED timestamp without time zone NOT NULL, " + + "DTMODIFIED timestamp without time zone NOT NULL, " + + "UUID varchar(40) NOT NULL, " + + "VERSION int NOT NULL," + + "COURSEID varchar(32) NOT NULL, " + + "FOLDERID varchar(32) NOT NULL, " + + "AVAILABLE boolean NOT NULL," + + "COURSEAVAILABLE boolean NOT NULL, " + + "CONTENTID varchar(32) NULL, " + + "COURSENAME varchar(512) NULL, " + + "BBDESC varchar(512) NULL, " + + "DTACCESSED timestamp without time zone NULL, " + + "ATTNAME varchar(512) NULL" + + ")"; + //@formatter:on + SqlUtil.runSql(sql, null); + + SqlUtil.runSql("CREATE INDEX eqcontent_course ON equellacontent(INSTURL, COURSEID)", null); + // cannot be UNIQUE since same item could be added to same folder + // (in theory) + SqlUtil.runSql("CREATE INDEX eqcontent_all ON equellacontent(INSTURL, UUID, VERSION, COURSEID, FOLDERID)", + null); + SqlUtil.runSql("CREATE INDEX eqcontent_uuidversion ON equellacontent(INSTURL, UUID, VERSION)", null); + SqlUtil.runSql("CREATE INDEX eqcontent_available ON equellacontent(AVAILABLE, COURSEAVAILABLE)", null); + SqlUtil.runSql("CREATE INDEX eqcontent_content ON equellacontent(INSTURL, CONTENTID)", null); + } } - private static void modifyDatabaseAddContentAndCourseName() { - if (getDatabase().isOracle()) { + private static void modifyDatabaseAddContentAndCourseName() + { + final SqlUtil.DbType t = SqlUtil.getDatabaseType(); + if( t == SqlUtil.DbType.ORACLE ) + { // debug("Oracle DB detected"); - // @formatter:off - final String sql = "ALTER TABLE equellacontent ADD (" + "CONTENTID NVARCHAR2(32) NULL, " - + "COURSENAME NVARCHAR2(512) NULL" + ")"; - // @formatter:on + //@formatter:off + final String sql = "ALTER TABLE equellacontent ADD (" + + "CONTENTID NVARCHAR2(32) NULL, " + + "COURSENAME NVARCHAR2(512) NULL" + + ")"; + //@formatter:on SqlUtil.runSql(sql, null); SqlUtil.runSql("CREATE INDEX eqcontent_content ON equellacontent(INSTURL, CONTENTID)", null); - } else { + } + else if( t == SqlUtil.DbType.SQL_SERVER ) + { // debug("SQL Server DB detected"); - // @formatter:off - final String sql = "ALTER TABLE dbo.equellacontent ADD " + "CONTENTID nvarchar(32) NULL, " - + "COURSENAME nvarchar(512) NULL"; - // @formatter:on + //@formatter:off + final String sql = "ALTER TABLE dbo.equellacontent ADD " + + "CONTENTID nvarchar(32) NULL, " + + "COURSENAME nvarchar(512) NULL"; + //@formatter:on SqlUtil.runSql(sql, null); SqlUtil.runSql("CREATE INDEX eqcontent_content ON dbo.equellacontent(INSTURL, CONTENTID)", null); } + else if( t == SqlUtil.DbType.POSTGRES ) + { + // N/A. There won't be any Postgres DBs this old + } } - private static void modifyDatabaseAddDescription() { - final String sql; - if (getDatabase().isOracle()) { + private static void modifyDatabaseAddDescription() + { + final SqlUtil.DbType t = SqlUtil.getDatabaseType(); + String sql = null; + if( t == SqlUtil.DbType.ORACLE ) + { sql = "ALTER TABLE equellacontent ADD (BBDESC NVARCHAR2(512) NULL)"; - } else { + } + else if( t == SqlUtil.DbType.SQL_SERVER ) + { sql = "ALTER TABLE dbo.equellacontent ADD BBDESC nvarchar(512) NULL"; } - SqlUtil.runSql(sql, null); + else if( t == SqlUtil.DbType.POSTGRES ) + { + // N/A. There won't be any Postgres DBs this old + } + if( sql != null ) + { + SqlUtil.runSql(sql, null); + } } - private static void modifyDatabaseAddDateAccessed() { - final String sql; - if (getDatabase().isOracle()) { + private static void modifyDatabaseAddDateAccessed() + { + final SqlUtil.DbType t = SqlUtil.getDatabaseType(); + String sql = null; + if( t == SqlUtil.DbType.ORACLE ) + { sql = "ALTER TABLE equellacontent ADD (" + COL_DATE_ACCESSED + " DATE NULL)"; - } else { + } + else if( t == SqlUtil.DbType.SQL_SERVER ) + { sql = "ALTER TABLE dbo.equellacontent ADD " + COL_DATE_ACCESSED + " datetime NULL"; } - SqlUtil.runSql(sql, null); + else if( t == SqlUtil.DbType.POSTGRES ) + { + // N/A. There won't be any Postgres DBs this old + } + if( sql != null ) + { + SqlUtil.runSql(sql, null); + } } - private static void modifyDatabaseAddAttachmentName() { - final String sql; - if (getDatabase().isOracle()) { + private static void modifyDatabaseAddAttachmentName() + { + final SqlUtil.DbType t = SqlUtil.getDatabaseType(); + String sql = null; + if( t == SqlUtil.DbType.ORACLE ) + { sql = "ALTER TABLE equellacontent ADD (" + COL_ATTACHMENT_NAME + " NVARCHAR2(512) NULL)"; - } else { + } + else if( t == SqlUtil.DbType.SQL_SERVER ) + { sql = "ALTER TABLE dbo.equellacontent ADD " + COL_ATTACHMENT_NAME + " nvarchar(512) NULL"; } - SqlUtil.runSql(sql, null); - } - - private static BbDatabase getDatabase() { - final DatabaseContainer dbContainer = (DatabaseContainer) PersistenceServiceFactory.getInstance() - .getDbPersistenceManager().getContainer(); - return dbContainer.getBbDatabase(); + else if( t == SqlUtil.DbType.POSTGRES ) + { + // N/A. There won't be any Postgres DBs this old + } + if( sql != null ) + { + SqlUtil.runSql(sql, null); + } } - private static String trim(String text, int maxlen) { - if (text != null && text.length() > maxlen) { + private static String trim(String text, int maxlen) + { + if( text != null && text.length() > maxlen ) + { return text.substring(0, maxlen); } return text; diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/RegistrationUtil.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/RegistrationUtil.java index ae5141e..0da1cba 100644 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/RegistrationUtil.java +++ b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/RegistrationUtil.java @@ -619,7 +619,12 @@ else if( t == SqlUtil.DbType.POSTGRES ) + "COURSEID varchar(32) NOT NULL, " + "FOLDERID varchar(32) NOT NULL, " + "AVAILABLE boolean NOT NULL," - + "COURSEAVAILABLE boolean NOT NULL" + + "COURSEAVAILABLE boolean NOT NULL, " + + "CONTENTID varchar(32) NULL, " + + "COURSENAME varchar(512) NULL, " + + "BBDESC varchar(512) NULL, " + + "DTACCESSED timestamp without time zone NULL, " + + "ATTNAME varchar(512) NULL" + ")"; //@formatter:on SqlUtil.runSql(sql, null); @@ -631,6 +636,7 @@ else if( t == SqlUtil.DbType.POSTGRES ) null); SqlUtil.runSql("CREATE INDEX eqcontent_uuidversion ON equellacontent(INSTURL, UUID, VERSION)", null); SqlUtil.runSql("CREATE INDEX eqcontent_available ON equellacontent(AVAILABLE, COURSEAVAILABLE)", null); + SqlUtil.runSql("CREATE INDEX eqcontent_content ON equellacontent(INSTURL, CONTENTID)", null); } } From b1fab1742af3971573d476d69b859d982c9c9977 Mon Sep 17 00:00:00 2001 From: Aaron Holland Date: Thu, 21 Feb 2019 12:13:21 +1100 Subject: [PATCH 09/14] Remove contextTag from common --- build.gradle | 4 +- gbFixer/public_html/WEB-INF/tle.tld | 2 +- linkFixer/public_html/WEB-INF/tle.tld | 2 +- oeqAuditB2/src/main/webapp/taglibs/tle.tld | 2 +- .../blackboard/buildingblock}/ContextTag.java | 2 +- oeqPrimaryB2/src/main/webapp/taglibs/tle.tld | 2 +- .../blackboard/common/BbContext.java | 53 -- .../blackboard/common/BbLogger.java | 113 --- .../integration/blackboard/common/BbUtil.java | 276 ------ .../blackboard/common/PathUtils.java | 217 ----- .../blackboard/common/SqlUtil.java | 290 ------ .../common/content/ContentUtil.java | 409 --------- .../blackboard/common/content/ItemInfo.java | 119 --- .../blackboard/common/content/ItemKey.java | 100 --- .../blackboard/common/content/ItemUtil.java | 164 ---- .../common/content/LegacyItemUtil.java | 183 ---- .../common/content/PlacementUtil.java | 167 ---- .../common/content/RegistrationUtil.java | 749 ---------------- .../propbag/BadCharacterFilterReader.java | 237 ----- .../propbag/BadCharacterFilterWriter.java | 64 -- .../blackboard/common/propbag/DOMHelper.java | 168 ---- .../blackboard/common/propbag/PropBagMin.java | 845 ------------------ .../common/propbag/UnicodeReader.java | 104 --- 23 files changed, 7 insertions(+), 4265 deletions(-) rename {oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common => oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock}/ContextTag.java (92%) delete mode 100644 oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/BbContext.java delete mode 100644 oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/BbLogger.java delete mode 100644 oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/BbUtil.java delete mode 100644 oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/PathUtils.java delete mode 100644 oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/SqlUtil.java delete mode 100644 oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/ContentUtil.java delete mode 100644 oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/ItemInfo.java delete mode 100644 oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/ItemKey.java delete mode 100644 oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/ItemUtil.java delete mode 100644 oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/LegacyItemUtil.java delete mode 100644 oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/PlacementUtil.java delete mode 100644 oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/RegistrationUtil.java delete mode 100644 oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/BadCharacterFilterReader.java delete mode 100644 oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/BadCharacterFilterWriter.java delete mode 100644 oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/DOMHelper.java delete mode 100644 oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/PropBagMin.java delete mode 100644 oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/UnicodeReader.java diff --git a/build.gradle b/build.gradle index 2d8dd59..606e98a 100644 --- a/build.gradle +++ b/build.gradle @@ -144,12 +144,12 @@ project(':oeqPrimaryWS') { task buildWS(type: Jar) { dependsOn classes - //dependsOn zipJavadoc + dependsOn zipJavadoc group 'oEQ' description 'Compiles the source and packages the web service as a JAR' baseName = 'oeq-blackboard-primary-ws' with jar - //from (project(':oeqCommon').sourceSets.main.output) // Include the common classes as well. + from (project(':oeqCommon').sourceSets.main.output) // Include the common classes as well. from ('src/main/manifests/bb-manifest.xml'){ // set the B2 version and place in the war into("META-INF") } diff --git a/gbFixer/public_html/WEB-INF/tle.tld b/gbFixer/public_html/WEB-INF/tle.tld index ab8b896..84cf227 100644 --- a/gbFixer/public_html/WEB-INF/tle.tld +++ b/gbFixer/public_html/WEB-INF/tle.tld @@ -21,7 +21,7 @@ PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" context - com.dytech.edge.blackboard.net.ContextTag + org.apereo.openequella.integration.blackboard.buildingblock.ContextTag JSP diff --git a/linkFixer/public_html/WEB-INF/tle.tld b/linkFixer/public_html/WEB-INF/tle.tld index ba9cb40..7a829da 100644 --- a/linkFixer/public_html/WEB-INF/tle.tld +++ b/linkFixer/public_html/WEB-INF/tle.tld @@ -21,7 +21,7 @@ PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" context - com.dytech.edge.blackboard.net.ContextTag + org.apereo.openequella.integration.blackboard.buildingblock.ContextTag JSP diff --git a/oeqAuditB2/src/main/webapp/taglibs/tle.tld b/oeqAuditB2/src/main/webapp/taglibs/tle.tld index cc95622..7a829da 100644 --- a/oeqAuditB2/src/main/webapp/taglibs/tle.tld +++ b/oeqAuditB2/src/main/webapp/taglibs/tle.tld @@ -21,7 +21,7 @@ PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" context - org.apereo.openequella.integration.blackboard.common.ContextTag + org.apereo.openequella.integration.blackboard.buildingblock.ContextTag JSP diff --git a/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/ContextTag.java b/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/ContextTag.java similarity index 92% rename from oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/ContextTag.java rename to oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/ContextTag.java index 8047a20..6eca0b0 100644 --- a/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/ContextTag.java +++ b/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/ContextTag.java @@ -1,4 +1,4 @@ -package org.apereo.openequella.integration.blackboard.common; +package org.apereo.openequella.integration.blackboard.buildingblock; import javax.servlet.http.HttpServletRequest; import javax.servlet.jsp.tagext.Tag; diff --git a/oeqPrimaryB2/src/main/webapp/taglibs/tle.tld b/oeqPrimaryB2/src/main/webapp/taglibs/tle.tld index ca9c88c..31e219f 100644 --- a/oeqPrimaryB2/src/main/webapp/taglibs/tle.tld +++ b/oeqPrimaryB2/src/main/webapp/taglibs/tle.tld @@ -8,7 +8,7 @@ TLE Taglib context - org.apereo.openequella.integration.blackboard.common.ContextTag + org.apereo.openequella.integration.blackboard.buildingblock.ContextTag JSP diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/BbContext.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/BbContext.java deleted file mode 100644 index 52e1811..0000000 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/BbContext.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.apereo.openequella.integration.blackboard.common; - -import blackboard.persist.BbPersistenceManager; -import blackboard.platform.context.ContextManager; -import blackboard.platform.context.ContextManagerFactory; -import blackboard.platform.vxi.data.VirtualInstallation; - -import com.google.common.base.Throwables; - -/** - * @author Aaron - */ -// @NonNullByDefault -public class BbContext { - /* @Nullable */ - private static BbContext instance; - private static final Object instanceLock = new Object(); - - private final ContextManager context; - private final BbPersistenceManager bbPm; - - @SuppressWarnings("null") - public static BbContext instance() { - if (instance == null) { - synchronized (instanceLock) { - if (instance == null) { - instance = new BbContext(); - } - } - } - return instance; - } - - @SuppressWarnings("nls") - private BbContext() { - try { - context = ContextManagerFactory.getInstance(); - final VirtualInstallation vi = context.getContext().getVirtualInstallation(); - bbPm = BbPersistenceManager.getInstance(vi); - } catch (Exception e) { - BbLogger.instance().logError("Couldn't init BbContext", e); - throw Throwables.propagate(e); - } - } - - public ContextManager getContextManager() { - return context; - } - - public BbPersistenceManager getPersistenceManager() { - return bbPm; - } -} diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/BbLogger.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/BbLogger.java deleted file mode 100644 index 1518c7e..0000000 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/BbLogger.java +++ /dev/null @@ -1,113 +0,0 @@ -package org.apereo.openequella.integration.blackboard.common; - -import java.util.Map; -import java.util.Map.Entry; - -import blackboard.platform.log.LogService; -import blackboard.platform.log.LogServiceFactory; - -// Wraps the common logger, and exposes the ability to soft 'change' -// Logging Levels -public class BbLogger { - - // public static void main(String[] args) { - // Map m = new HashMap<>(); - // m.put("key1", "value1"); - // for (Entry e : m.entrySet()) { - // System.out.println("K=[" + e.getKey() + "], V=[" + e.getValue() + "]"); - // } - // } - - private static final String SIG = "oeqInteg - "; - private static final LogService LOGGER = LogServiceFactory.getInstance(); - - // Maybe not the best way to handle this. - private static BbLogger instance; - private static final Object instanceLock = new Object(); - - private LogLevel logLevel = LogLevel.NotSet; - - // If NotSet is chosen, the logger will use the standard logger - // levels for messaging. If any other value is chosen, the logger - // will still honor the log levels, filtered by the level set. - public enum LogLevel { - NotSet, Warn, Info, Debug, SqlTrace, Trace, - } - - @SuppressWarnings("null") - public static BbLogger instance() { - if (instance == null) { - synchronized (instanceLock) { - if (instance == null) { - instance = new BbLogger(); - } - } - } - return instance; - } - - private BbLogger() { - - } - - public synchronized void setLoggingLevel(LogLevel level) { - logLevel = level; - } - - public void logTrace(String msg, Map m) { - logTrace(msg); - for (Entry e : m.entrySet()) { - logTrace("K=[" + e.getKey() + "], V=[" + e.getValue() + "]"); - } - } - - public void logTrace(String msg) { - // if (logLevel == LogLevel.NotSet) { - // LOGGER.logAudit(SIG + msg); - // } else if (logLevel == LogLevel.Trace) { - LOGGER.logWarning(SIG + "Trace - " + msg); - // } - } - - public void logSqlTrace(String msg) { - if (logLevel == LogLevel.NotSet) { - LOGGER.logAudit(SIG + msg); - } else if ((logLevel == LogLevel.SqlTrace) || (logLevel == LogLevel.Trace)) { - LOGGER.logWarning(SIG + "SqlTrace - " + msg); - } - } - - public void logDebug(String msg) { - if (logLevel == LogLevel.NotSet) { - LOGGER.logDebug(SIG + msg); - } else if ((logLevel == LogLevel.Debug) || (logLevel == LogLevel.SqlTrace) || (logLevel == LogLevel.Trace)) { - LOGGER.logWarning(SIG + "Debug - " + msg); - } - } - - public void logInfo(String msg) { - if (logLevel == LogLevel.NotSet) { - LOGGER.logInfo(SIG + msg); - } else if ((logLevel == LogLevel.Info) || (logLevel == LogLevel.Debug) || (logLevel == LogLevel.SqlTrace) - || (logLevel == LogLevel.Trace)) { - LOGGER.logWarning(SIG + "Info - " + msg); - } - } - - public void logWarn(String msg) { - LOGGER.logWarning(SIG + msg); - } - - public void logError(String msg) { - LOGGER.logError(SIG + msg); - } - - public void logError(String msg, Exception t) { - LOGGER.logError(SIG + msg, t); - } - - public static String getLoggingDetails() { - return "LogFileName=[" + LOGGER.getLogFileName() + "], LogName=[" + LOGGER.getLogName() + "], LogVerbosity=[" - + LOGGER.getVerbosityLevel().toExternalString() + "]"; - } -} diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/BbUtil.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/BbUtil.java deleted file mode 100644 index a5aa3ed..0000000 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/BbUtil.java +++ /dev/null @@ -1,276 +0,0 @@ -package org.apereo.openequella.integration.blackboard.common; - -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.List; -import java.util.StringTokenizer; - -import com.google.common.base.Strings; -import com.google.common.base.Throwables; - -import blackboard.base.BbList; -import blackboard.data.content.Content; -import blackboard.data.course.Course; -import blackboard.data.navigation.CourseToc; -import blackboard.persist.BbPersistenceManager; -import blackboard.persist.DataType; -import blackboard.persist.Id; -import blackboard.persist.KeyNotFoundException; -import blackboard.persist.PersistenceException; -import blackboard.persist.content.ContentDbLoader; -import blackboard.persist.course.CourseDbLoader; -import blackboard.persist.navigation.CourseTocDbLoader; -import blackboard.platform.config.ConfigurationServiceFactory; -import blackboard.platform.plugin.PlugInUtil; -import blackboard.platform.ws.AxisHelpers; - -/** - * @author Aaron - */ -@SuppressWarnings("nls") -// @NonNullByDefault -public class BbUtil { - public static final String HANDLE = "tle"; - public static final String VENDOR = "dych"; - public static final String CONTENT_HANDLER = "resource/tle-resource"; - public static final String CONTEXT_TAG = "@X@"; - public static final String CONTENT_ID = "content_id"; - public static final String CONTENT_ID_PLACEHOLDER = CONTEXT_TAG + "content.pk_string" + CONTEXT_TAG; - public static final String COURSE_ID = "course_id"; - public static final String COURSE_ID_PLACEHOLDER = CONTEXT_TAG + "course.pk_string" + CONTEXT_TAG; - - private static int majorVersionNumber; - private static int minorVersionNumber; - private static int revisionNumber; - private static String bbVersionString; - - static { - bbVersionString = ConfigurationServiceFactory.getInstance().getBbProperty("bbconfig.version.number"); - final StringTokenizer st = new StringTokenizer(bbVersionString, "."); - if (st.hasMoreTokens()) { - majorVersionNumber = Integer.parseInt(st.nextToken()); - try { - minorVersionNumber = Integer.parseInt(st.nextToken()); - } catch (Exception e) { - // so be it - minorVersionNumber = -1; - } - - try { - revisionNumber = Integer.parseInt(st.nextToken()); - } catch (Exception e) { - // so be it - revisionNumber = -1; - } - } - } - - public static String getBbVersionString() { - return bbVersionString; - } - - /** - * Called from config.jsp - * - * @return - */ - public static int getMajorVersionNumber() { - return majorVersionNumber; - } - - /** - * Called from config.jsp - * - * @return - */ - public static int getMinorVersionNumber() { - return minorVersionNumber; - } - - public static int getRevisionNumber() { - return revisionNumber; - } - - private BbUtil() { - throw new Error(); - } - - public static String getParamBase() { - return new StringBuilder(CONTENT_ID).append("=").append(CONTENT_ID_PLACEHOLDER).append("&").append(COURSE_ID) - .append("=").append(COURSE_ID_PLACEHOLDER).toString(); - } - - public static String urlEncode(String url) { - try { - return URLEncoder.encode(url, "UTF-8"); - } catch (final UnsupportedEncodingException e) { - // Never happen - return url; - } - } - - public static String getBlockRelativePath() { - try { - return PlugInUtil.getUriStem(VENDOR, HANDLE); - } catch (final Exception t) { - BbLogger.instance().logError("Error getting relative path", t); - throw Throwables.propagate(t); - } - } - - public static Id getCourseId(String courseId) { - return getId(courseId, blackboard.data.course.Course.DATA_TYPE); - } - - public static Id getContentId(String contentId) { - return getId(contentId, blackboard.data.content.Content.DATA_TYPE); - } - - public static Id getFolderId(String folderId) { - return getId(folderId, blackboard.data.content.ContentFolder.DATA_TYPE); - } - - public static Id getUserId(String userId) { - return getId(userId, blackboard.data.user.User.DATA_TYPE); - } - - public static Id getId(String id, DataType type) { - try { - return BbContext.instance().getPersistenceManager().generateId(type, id); - } catch (PersistenceException e) { - AxisHelpers.throwWSException("EQ002", e.getMessage()); - // Should not reach here - throw Throwables.propagate(e); - } - } - - /* @Nullable */ - public static Content loadContent(ContentDbLoader contentDbLoader, String id) throws PersistenceException { - return loadContent(contentDbLoader, BbUtil.getContentId(id)); - } - - /* @Nullable */ - public static Content loadContent(ContentDbLoader contentDbLoader, Id id) throws PersistenceException { - try { - return contentDbLoader.loadById(id); - } - // KNF..uh huh uh huh uh huh. KNF's gunna rock ya - // http://www.youtube.com/watch?v=LXEOESuiYcA - catch (KeyNotFoundException knf) { - return null; - } - } - - /* @Nullable */ - public static Course loadCourse(CourseDbLoader courseDbLoader, String id) throws PersistenceException { - return loadCourse(courseDbLoader, BbUtil.getCourseId(id)); - } - - /* @Nullable */ - public static Course loadCourse(CourseDbLoader courseDbLoader, Id id) throws PersistenceException { - try { - return courseDbLoader.loadById(id); - } - // KNF..uh huh uh huh uh huh. KNF's gunna rock ya - // http://www.youtube.com/watch?v=LXEOESuiYcA - catch (KeyNotFoundException knf) { - return null; - } - } - - public static Iterable getBbFoldersForCourse(Id courseId) throws PersistenceException { - final BbPersistenceManager pm = BbContext.instance().getPersistenceManager(); - final CourseTocDbLoader courseTocDbLoader = (CourseTocDbLoader) pm.getLoader(CourseTocDbLoader.TYPE); - final ContentDbLoader contentDbLoader = (ContentDbLoader) pm.getLoader(ContentDbLoader.TYPE); - - // This is the Information / Contents etc list displayed on the - // left when viewing a course. - // But surely there is only one per course? - final BbList bbcourseTocs = courseTocDbLoader.loadByCourseId(courseId); - final List folders = new ArrayList(); - - for (CourseToc bbcourseToc : bbcourseTocs) { - // list the content - final Id contentId = bbcourseToc.getContentId(); - if (contentId.isSet()) { - final Content courseContent = contentDbLoader.loadById(contentId); - if (courseContent.getIsFolder()) { - folders.add(courseContent); - } - } - } - return folders; - } - - /** - * If you need subfolders only you need to check content.isFolder for each - * returned content. - * - * @param pm - * @param folderId - * @return - * @throws PersistenceException - */ - public static Iterable getBbContentForFolder(Id folderId) throws PersistenceException { - final ContentDbLoader contentDbLoader = (ContentDbLoader) BbContext.instance().getPersistenceManager() - .getLoader(ContentDbLoader.TYPE); - return contentDbLoader.loadChildren(folderId); - } - - public static String ent(String szStr) { - if (Strings.isNullOrEmpty(szStr)) { - return ""; - } - - StringBuilder szOut = new StringBuilder(); - final char[] chars = szStr.toCharArray(); - for (final char ch : chars) { - switch (ch) { - case '<': - szOut.append("<"); - break; - - case '>': - szOut.append(">"); - break; - - case '&': - szOut.append("&"); - break; - - case '"': - szOut.append("""); - break; - - default: - // http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char - // regular displayable ASCII: - if (ch == 0xA || ch == 0xD || ch == 0x9 || (ch >= 0x20 && ch <= 0x007F)) { - szOut.append(ch); - } else if ((ch > 0x007F && ch <= 0xD7FF) || (ch >= 0xE000 && ch <= 0xFFFD) - || (ch >= 0x10000 && ch <= 0x10FFFF)) { - szOut.append("&#x"); - final String hexed = Integer.toHexString(ch); - // wooo, unrolled loops - switch (4 - hexed.length()) { - case 3: - case 2: - case 1: - szOut.append('0'); - break; - default: - break; - } - szOut.append(hexed); - szOut.append(';'); - } - // else we discard the character entirely. - // It CANNOT be placed in XML - break; - } - } - - return szOut.toString(); - } -} diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/PathUtils.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/PathUtils.java deleted file mode 100644 index 6b782b8..0000000 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/PathUtils.java +++ /dev/null @@ -1,217 +0,0 @@ -package org.apereo.openequella.integration.blackboard.common; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; - -import com.google.common.base.Strings; - -@SuppressWarnings("nls") -public class PathUtils { - private PathUtils() { - throw new Error(); - } - - public static String urlPath(String... urlBits) { - return filePath(urlBits); - } - - /** - * @param pathBits - * @return A file path built out of pathBits. E.g. bit/bit2/bit3 - */ - public static String filePath(String... pathBits) { - final List nonEmptyBits = new ArrayList(); - for (String bit : pathBits) { - if (!Strings.isNullOrEmpty(bit)) { - nonEmptyBits.add(removeTrailingSlash(bit)); - } - } - if (nonEmptyBits.size() == 0) { - return ""; - } - if (nonEmptyBits.size() == 1) { - return nonEmptyBits.get(0); - } - - String bit0 = nonEmptyBits.get(0); - final StringBuilder path = new StringBuilder(bit0); - for (int i = 1; i < nonEmptyBits.size(); i++) { - String bit = nonEmptyBits.get(i); - if (!Strings.isNullOrEmpty(bit)) { - if (!bit.startsWith("/")) { - // We have to use forward slashes because some file paths - // are used in URLs - path.append('/'); - } - path.append(bit); - } - } - return path.toString(); - } - - // - // /** - // * Breaks filename into a path with no file extension, and the extension - // * (with no dot) - // * - // * @param filename - // * @return - // */ - // public static Pair fileParts(String filename) - // { - // String fullSrcPath = filename; - // String fileNoExt = fullSrcPath; - // String ext = ""; - // int dot = fullSrcPath.lastIndexOf('.'); - // if( dot > 0 ) - // { - // fileNoExt = fullSrcPath.substring(0, dot); - // ext = fullSrcPath.substring(dot + 1); - // } - // return new Pair(fileNoExt, ext); - // } - - public static String getFilenameFromFilepath(String filepath) { - String fixedPath = removeTrailingSlash(filepath); - - int ind = fixedPath.lastIndexOf('/'); - if (ind == -1) { - return fixedPath; - } - return fixedPath.substring(ind + 1); - } - - private static String removeTrailingSlash(String filepath) { - String fixedPath = filepath.replaceAll("\\\\", "/"); - while (fixedPath.endsWith("/")) { - fixedPath = fixedPath.substring(0, fixedPath.length() - 1); - } - return fixedPath; - } - - public static String getParentFolderFromFilepath(String filepath) { - String fixedPath = removeTrailingSlash(filepath); - - int ind = fixedPath.lastIndexOf('/'); - if (ind == -1) - - { - return null; - } - return fixedPath.substring(0, ind); - } - - public static String fileencode(String szStr) { - StringBuilder szOut = new StringBuilder(); - for (int i = 0; i < szStr.length(); i++) { - char ch = szStr.charAt(i); - switch (ch) { - case ':': - case '*': - case '?': - case '"': - case '<': - case '>': - case '|': - case '%': - szOut.append('%'); - int intval = ch; - szOut.append(String.format("%02x", intval)); - break; - default: - szOut.append(ch); - } - } - return szOut.toString(); - } - - /** - * Stolen from org.apache.catalina.util.URL - * - * @param path - */ - public static String relativePath(String base, String path) { - // Create a place for the normalized path - String normalizedPath = path; - - if (Strings.isNullOrEmpty(normalizedPath)) { - return base; - } else if (normalizedPath.charAt(0) != '/') { - try { - normalizedPath = new URL(new URL("http", "localhost", base), normalizedPath).getPath(); - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } - } - - if (normalizedPath.equals("/.")) { - return "/"; - } - - // Normalize the slashes and add leading slash if necessary - if (normalizedPath.indexOf('\\') >= 0) { - normalizedPath = normalizedPath.replace('\\', '/'); - } - if (!normalizedPath.startsWith("/")) { - normalizedPath = "/" + normalizedPath; - } - - // Resolve occurrences of "//" in the normalized path - while (true) { - int index = normalizedPath.indexOf("//"); - if (index < 0) { - break; - } - normalizedPath = normalizedPath.substring(0, index) + normalizedPath.substring(index + 1); - } - - // Resolve occurrences of "/./" in the normalized path - while (true) { - int index = normalizedPath.indexOf("/./"); - if (index < 0) { - break; - } - normalizedPath = normalizedPath.substring(0, index) + normalizedPath.substring(index + 2); - } - - // Resolve occurrences of "/../" in the normalized path - while (true) { - int index = normalizedPath.indexOf("/../"); - if (index < 0) { - break; - } - if (index == 0) { - throw new RuntimeException("Invalid relative URL reference"); - } - int index2 = normalizedPath.lastIndexOf('/', index - 1); - normalizedPath = normalizedPath.substring(0, index2) + normalizedPath.substring(index + 3); - } - - // Resolve occurrences of "/." at the end of the normalized path - if (normalizedPath.endsWith("/.")) { - normalizedPath = normalizedPath.substring(0, normalizedPath.length() - 1); - } - - // Resolve occurrences of "/.." at the end of the normalized path - if (normalizedPath.endsWith("/..")) { - int index = normalizedPath.length() - 3; - int index2 = normalizedPath.lastIndexOf('/', index - 1); - if (index2 < 0) { - throw new RuntimeException("Invalid relative URL reference"); - } - normalizedPath = normalizedPath.substring(0, index2 + 1); - } - - return normalizedPath; - } - - public static String extension(String filename) { - int extInd = filename.lastIndexOf('.'); - if (extInd != -1) { - return filename.substring(extInd + 1); - } - return ""; - } -} diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/SqlUtil.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/SqlUtil.java deleted file mode 100644 index c9ab0e8..0000000 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/SqlUtil.java +++ /dev/null @@ -1,290 +0,0 @@ -package org.apereo.openequella.integration.blackboard.common; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Timestamp; -import java.util.Collections; -import java.util.List; - -import blackboard.db.ConnectionManager; -import blackboard.db.DbUtil; - -/** - * @author Aaron - */ -@SuppressWarnings("nls") -// @NonNullByDefault -public class SqlUtil -{ - public enum DbType - { - UNKNOWN, SQL_SERVER, ORACLE, POSTGRES - } - - private static DbType type = DbType.UNKNOWN; - - private static final String TABLE = "equellacontent"; - - public static DbType getDatabaseType() - { - if( type == DbType.UNKNOWN ) - { - try - { - // Postgres test - runSql("SELECT current_database()", null); - type = DbType.POSTGRES; - } - catch( Exception e ) - { - try - { - // Oracle test - runSql("SELECT 1 FROM DUAL", null); - type = DbType.ORACLE; - } - catch( Exception e2 ) - { - try - { - // SQL server test - runSql("SELECT owner, table_name FROM all_tables", null); - type = DbType.SQL_SERVER; - } - catch( Exception e3 ) - { - type = DbType.UNKNOWN; - } - } - } - } - return type; - } - - public static StringBuilder select(String... columns) - { - final StringBuilder sql = new StringBuilder("SELECT "); - if( columns.length == 0 ) - { - sql.append("*"); - } - else - { - boolean first = true; - for( String col : columns ) - { - if( !first ) - { - sql.append(", "); - } - sql.append(col); - first = false; - } - } - return sql.append(" ").append(from()); - } - - public static StringBuilder update(String... columns) - { - final StringBuilder sql = new StringBuilder("UPDATE ").append(table()).append("SET "); - boolean first = true; - for( String col : columns ) - { - if( !first ) - { - sql.append(", "); - } - sql.append(col); - sql.append("=?"); - first = false; - } - return sql.append(" "); - } - - public static StringBuilder insert(String... columns) - { - StringBuilder sql = new StringBuilder("INSERT INTO ").append(table()).append("("); - boolean first = true; - for( String col : columns ) - { - if( !first ) - { - sql.append(", "); - } - sql.append(col); - first = false; - } - return sql.append(") "); - } - - public static boolean columnExists(String column) - { - try - { - final DbType t = getDatabaseType(); - SqlUtil.runSql("SELECT " + (t == DbType.SQL_SERVER ? "TOP 1 " : "") + " " + column + " FROM " + table() - + (t == DbType.ORACLE ? " WHERE rownum < 1" : (t == DbType.POSTGRES ? " LIMIT 1" : "")), null); - return true; - } - catch( Exception e ) - { - // debug("Failed reading column "+ column +" from DB"); - return false; - } - } - - public static String delete() - { - return "DELETE " + from(); - } - - public static String from() - { - return "FROM " + table(); - } - - public static String table() - { - return schema() + TABLE + " "; - } - - public static String schema() - { - final DbType t = getDatabaseType(); - return (t != DbType.SQL_SERVER ? "" : "dbo."); - } - - public static List runSql(String sql, /* @Nullable */ResultProcessor processor, Object... params) - { - PreparedStatement stmt = null; - List result = null; - - //BbUtil.sqlTrace("Getting connection manager"); - ConnectionManager connMgr = DbUtil.safeGetBbDatabase().getConnectionManager(); - - Connection conn = null; - try - { - //BbUtil.sqlTrace("Getting connection"); - conn = connMgr.getConnection(); - - //BbUtil.sqlTrace("Creating statement"); - stmt = conn.prepareStatement(sql); - - //BbUtil.sqlTrace("Has " + params.length + " params"); - int index = 1; - for( Object param : params ) - { - if( param instanceof OptionalParam ) - { - final OptionalParam opt = (OptionalParam) param; - if( opt.isUsed() ) - { - setParam(stmt, index++, opt.getValue()); - } - } - else - { - setParam(stmt, index++, param); - } - } - - //BbUtil.sqlTrace("Executing: " + sql); - if( processor != null ) - { - result = processor.getResults(stmt.executeQuery()); - } - else - { - stmt.execute(); - result = (List) Collections.singletonList(stmt.getUpdateCount()); - } - //BbUtil.sqlTrace("Success!!"); - return result; - } - catch( Throwable t ) - { - //BbUtil.error("Failed to runSql", t); - throw new RuntimeException(t); - } - finally - { - //BbUtil.sqlTrace("Closing statement"); - DbUtil.closeStatement(stmt); - //BbUtil.sqlTrace("Releasing connection"); - ConnectionManager.releaseDefaultConnection(conn); - } - } - - public static void setParam(PreparedStatement stmt, int index, /* @Nullable */Object param) throws SQLException - { - if( param instanceof String ) - { - //BbUtil.sqlTrace("Setting param string[" + index + "] = " + param); - stmt.setString(index, (String) param); - } - else if( param instanceof Integer ) - { - //BbUtil.sqlTrace("Setting param int[" + index + "] = " + param); - stmt.setInt(index, (Integer) param); - } - else if( param instanceof Timestamp ) - { - //BbUtil.sqlTrace("Setting param timestamp[" + index + "] = " + param); - stmt.setTimestamp(index, (Timestamp) param); - } - else if( param instanceof Boolean ) - { - boolean pval = (Boolean) param; - //BbUtil.sqlTrace("Setting param boolean[" + index + "] = " + pval); - final SqlUtil.DbType t = SqlUtil.getDatabaseType(); - if (t == DbType.POSTGRES) - { - // TODO: this should work on all types? - stmt.setBoolean(index, pval); - } - else - { - stmt.setInt(index, pval ? 1 : 0); - } - } - else if( param == null ) - { - //BbUtil.sqlTrace("Setting param ?[" + index + "] = null"); - stmt.setString(index, null); - } - else - { - throw new RuntimeException("Parameter " + index + " is an unhandled type: " + param.getClass().getName()); - } - } - - public interface ResultProcessor - { - List getResults(ResultSet results) throws SQLException; - } - - public static class OptionalParam - { - private final T value; - private final boolean used; - - public OptionalParam(T value, boolean used) - { - this.value = value; - this.used = used; - } - - public T getValue() - { - return value; - } - - public boolean isUsed() - { - return used; - } - } -} diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/ContentUtil.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/ContentUtil.java deleted file mode 100644 index 8cf7036..0000000 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/ContentUtil.java +++ /dev/null @@ -1,409 +0,0 @@ -package org.apereo.openequella.integration.blackboard.common.content; - -import java.util.Arrays; -import java.util.Calendar; -import java.util.Date; -import java.util.List; - -import org.apache.commons.httpclient.HttpClient; -import org.apache.commons.httpclient.methods.GetMethod; - -import blackboard.base.FormattedText; -import blackboard.data.ExtendedData; -import blackboard.data.blti.BasicLTIPlacement; -import blackboard.data.content.Content; -import blackboard.data.content.CourseDocument; -import blackboard.data.course.Course; -import blackboard.persist.Id; -import blackboard.persist.PersistenceException; -import blackboard.persist.content.ContentDbLoader; -import blackboard.persist.content.ContentDbPersister; -import blackboard.platform.gradebook2.GradableItem; -import blackboard.platform.gradebook2.GradableItemManager; -import blackboard.platform.gradebook2.GradebookManagerFactory; -import blackboard.platform.security.authentication.BbSecurityException; - -import com.google.common.base.Strings; -import com.google.common.base.Throwables; -import org.apereo.openequella.integration.blackboard.common.BbContext; -import org.apereo.openequella.integration.blackboard.common.BbLogger; -import org.apereo.openequella.integration.blackboard.common.BbUtil; -import org.apereo.openequella.integration.blackboard.common.PathUtils; - -/** - * @author Aaron - */ -@SuppressWarnings("nls") -public class ContentUtil { - private static ContentUtil instance; - private static final Object instanceLock = new Object(); - - // TODO: privatise - public static final String FIELD_DESCRIPTION = "description"; - public static final String FIELD_UUID = "uuid"; - public static final String FIELD_VERSION = "version"; - /** - * This is a *relative* URL. I.e. it doesn't include the institution portion. - */ - public static final String FIELD_URL = "url"; - public static final String FIELD_ATTACHMENT_NAME = "attachmentName"; - public static final String FIELD_ATTACHMENT_UUID = "attachmentUuid"; - public static final String FIELD_MIME_TYPE = "mimeType"; - public static final String FIELD_ACTIVATIONUUID = "activationUuid"; - - private static final List GRADABLE_MIMES = Arrays.asList("equella/attachment-lti", "equella/qtitest", - "equella/scorm-package"); - - private ContentDbLoader contentLoader; - private ContentDbPersister contentPersister; - - public static ContentUtil instance() { - if (instance == null) { - synchronized (instanceLock) { - if (instance == null) { - instance = new ContentUtil(); - } - } - } - return instance; - } - - private ContentUtil() { - } - - /** - * @param course - * @param folderId - * @param uuid - * @param version - * @param url - * Relative URL - * @param title - * @param description - * @param attachmentUuid - * @param attachmentName - * @param mimeType - * @param equellaUrl - * @return - */ - public Content addContent(Course course, Id folderId, String uuid, int version, String url, String title, - String description, String attachmentUuid, String attachmentName, String mimeType, String equellaUrl, - boolean newWindow) { - try { - final Content content = new CourseDocument(); - content.setIsFolder(false); - content.setTitle(title); - content.setCourseId(course.getId()); - content.setParentId(folderId); - content.setIsAvailable(true); - content.setLaunchInNewWindow(true); - content.setContentHandler(BbUtil.CONTENT_HANDLER); - content.setPosition(-1); - content.setIsSequential(false); - content.setLaunchInNewWindow(newWindow); - - ExtendedData extendedData = content.getExtendedData(); - if (extendedData == null) { - extendedData = new ExtendedData(); - content.setExtendedData(extendedData); - } - extendedData.setValue(FIELD_UUID, uuid); - extendedData.setValue(FIELD_VERSION, Integer.toString(version)); - extendedData.setValue(FIELD_URL, url); - if (description != null) { - extendedData.setValue(FIELD_DESCRIPTION, description); - } - if (attachmentUuid != null) { - extendedData.setValue(FIELD_ATTACHMENT_UUID, attachmentUuid); - } - if (attachmentName != null) { - extendedData.setValue(FIELD_ATTACHMENT_NAME, attachmentName); - } - if (mimeType != null) { - extendedData.setValue(FIELD_MIME_TYPE, mimeType); - } - - return persistContent(content, course, equellaUrl, true); - } catch (Exception e) { - BbLogger.instance().logError("Error saving new content", e); - throw Throwables.propagate(e); - } - } - - public Content persistContent(Content content, Course course, String equellaUrl, boolean isNew) { - try { - ItemInfo itemInfo = ItemUtil.getItemInfo(content, course, content.getParentId(), equellaUrl); - final ItemKey itemKey = itemInfo.getItemKey(); - - final FormattedText text = new FormattedText( - ItemUtil.getHtml(equellaUrl, itemKey.getPage(), itemInfo.getAttachmentName(), itemInfo.getMimeType(), - content.getTitle(), itemInfo.getDescription(), content.getLaunchInNewWindow()), - FormattedText.Type.HTML); - content.setBody(text); - - BbLogger.instance().logTrace("persisting content for url " + itemKey.getPage()); - getContentPersister().persist(content); - BbLogger.instance().logTrace("content success!"); - - // need to do this again so the contentId doesn't get munged up - itemInfo = ItemUtil.getItemInfo(content, course, content.getParentId(), equellaUrl); - register(content, course, itemInfo, equellaUrl, isNew); - ensureGradableItem(content, isNew, true); - - return content; - } catch (Exception e) { - BbLogger.instance().logError("Error persisting content", e); - throw Throwables.propagate(e); - } - } - - public Content loadContent(Id contentId) { - try { - return getContentLoader().loadById(contentId); - } catch (Exception e) { - BbLogger.instance().logError("Error loading content", e); - throw Throwables.propagate(e); - } - } - - public Content loadContentForViewing(Id contentId) { - try { - final Content content = getContentLoader().loadById(contentId); - RegistrationUtil.updateDateAccessed(contentId.toExternalString(), new Date()); - ensureGradableItem(content, false, false); - return content; - } catch (Exception e) { - BbLogger.instance().logError("Error loading content for viewing", e); - throw Throwables.propagate(e); - } - } - - private void ensureGradableItem(Content content, boolean isNewContent, boolean modifyExisting) throws Exception { - BbLogger.instance().logTrace("ensureGradableItem"); - - final String mimeType = content.getExtendedData().getValue(FIELD_MIME_TYPE); - final BasicLTIPlacement ltiPlacement = PlacementUtil.getDefaultPlacement(); - if (ltiPlacement == null || !ltiPlacement.isAllowGrading() || !GRADABLE_MIMES.contains(mimeType)) { - return; - } - - final GradableItemManager gradableItemManager = GradebookManagerFactory - .getGradableItemManagerWithoutSecurityCheck(); - boolean foundExisting = false; - final GradableItem gradableItem; - if (isNewContent) { - gradableItem = createGradableItem(content); - } else { - GradableItem gitem = gradableItemManager.getGradebookItemByContentId(content.getId()); - if (gitem == null) { - gitem = createGradableItem(content); - } else { - BbLogger.instance().logTrace("gradable item already exists"); - foundExisting = true; - } - gradableItem = gitem; - } - if (modifyExisting || !foundExisting) { - gradableItem.setTitle(content.getTitle()); - gradableItem.setCourseId(content.getCourseId()); - gradableItem.setDateModified(Calendar.getInstance()); - try { - gradableItemManager.persistGradebookItem(gradableItem); - } catch (BbSecurityException e) { - BbLogger.instance().logError("Error saving gradableItem", e); - throw Throwables.propagate(e); - } - BbLogger.instance().logTrace("gradable item success!"); - } else { - BbLogger.instance().logTrace("not modifying existing gradable item"); - } - } - - @SuppressWarnings("deprecation") - private void register(Content content, Course course, ItemInfo itemInfo, String equellaUrl, boolean isNew) { - try { - // Register in EQUELLA table - BbLogger.instance().logTrace("Registering EQUELLA content in database"); - - final ItemKey itemKey = itemInfo.getItemKey(); - - BbLogger.instance().logDebug("Registering content " + itemKey + " which is " + (isNew ? "new" : "NOT new")); - - final Date createdDate = (content.getCreatedDate() == null ? new Date() : content.getCreatedDate().getTime()); - final Date modifiedDate = (content.getModifiedDate() == null ? createdDate : content.getModifiedDate().getTime()); - - RegistrationUtil.recordItem(itemKey, isNew, true, course.getIsAvailable(), content.getTitle(), - itemInfo.getDescription(), itemInfo.getAttachmentName(), createdDate, modifiedDate, course.getTitle()); - } catch (Exception t) { - BbLogger.instance().logError("An error occurred while registering content", t); - } - } - - private GradableItem createGradableItem(Content content) { - final GradableItem gradableItem = new GradableItem(); - gradableItem.setCourseContentId(content.getId()); - gradableItem.setScoreProviderHandle(BbUtil.CONTENT_HANDLER); - gradableItem.setVisibleInBook(true); - gradableItem.setVisibleToStudents(true); - gradableItem.setPoints(100); - gradableItem.setHideAttempt(false); - gradableItem.setDateAdded(Calendar.getInstance()); - gradableItem.setScorable(true); - return gradableItem; - } - - public void removeContent(Content content, Course course, String equellaUrl, String token) { - try { - final ItemInfo itemInfo = ItemUtil.getItemInfo(content, course, content.getParentId(), equellaUrl); - - unregister(itemInfo.getItemKey()); - deactivate(itemInfo, equellaUrl, token); - removeGradableItem(content); - - getContentPersister().deleteById(content.getId()); - } catch (Exception e) { - BbLogger.instance().logError("Error removing content", e); - throw Throwables.propagate(e); - } - } - - private void removeGradableItem(Content content) { - try { - // Delete gradebook item - final GradableItemManager gradableItemManager = GradebookManagerFactory - .getGradableItemManagerWithoutSecurityCheck(); - final GradableItem gradableItem = gradableItemManager.getGradebookItemByContentId(content.getId()); - if (gradableItem != null) { - try { - gradableItemManager.deleteGradebookItem(gradableItem.getId()); - } catch (BbSecurityException e) { - BbLogger.instance().logError("Error removing gradableItem", e); - throw Throwables.propagate(e); - } - } - } catch (Exception e) { - BbLogger.instance().logError("Error removing GradableItem", e); - throw Throwables.propagate(e); - } - } - - private void unregister(ItemKey itemKey) { - try { - final ContentRegisteredResponse reg = RegistrationUtil.contentIsRegistered(itemKey); - if (reg.isRegistered()) { - BbLogger.instance().logDebug("Unregistering content " + itemKey + ""); - RegistrationUtil.unrecordItem(reg.getId(), reg.getContentId()); - } else { - BbLogger.instance().logDebug("Content " + itemKey + " is not registered"); - } - } catch (Exception t) { - BbLogger.instance().logError("An error occurred while un-registering content", t); - } - } - - private void deactivate(ItemInfo itemInfo, String equellaUrl, String token) { - GetMethod method = null; - try { - final String requestUuid = itemInfo.getActivateRequestUuid(); - if (!Strings.isNullOrEmpty(requestUuid)) { - // FIXME: No option but tokens.... (for now) - final String url = PathUtils.urlPath(equellaUrl, "access/activationwebservice.do") + "?token=" - + BbUtil.urlEncode(token) + "&activationUuid=" + BbUtil.urlEncode(requestUuid); - method = new GetMethod(url); - - final HttpClient client = new HttpClient(); - client.getHttpConnectionManager().getParams().setConnectionTimeout(10000); - client.executeMethod(method.getHostConfiguration(), method, client.getState()); - final String response = method.getResponseBodyAsString(); - if (!response.equals("OK")) { - throw new Exception("Error deactivating attachment: " + response); - } - } - } catch (final Exception e) { - BbLogger.instance().logError("An error occurred while deactivating content", e); - throw Throwables.propagate(e); - } finally { - if (method != null) { - method.releaseConnection(); - } - } - } - - public boolean isLegacy(Content content) { - final FormattedText body = content.getBody(); - if (body != null && body.getFormattedText().startsWith("" - + "" - + "
\"
" - + "" - + "
{1}
\"
" - + "
\"*\"  {6}
"; - - // @formatter:on - - public static String getHtml(String serverUrl, String xmlString) { - PropBagMin xml = new PropBagMin(xmlString); - - String attTitle = xml.getNode("attachments/@selectedTitle"); - String selectedPage = xml.getNode("attachments/@selected"); - String selectedAttachmentType = xml.getNode("attachments/@selectedType"); - String description = xml.getNode("description"); - String title = xml.getNode("name"); - String url = selectedPage; - - String fileForIcon = selectedPage; - if ("link".equals(selectedAttachmentType)) { - fileForIcon = "http://"; - } - - if (serverUrl.endsWith("/")) { - serverUrl = serverUrl.substring(0, serverUrl.length() - 1); - } - - final String html = MessageFormat.format(HTML_TEMPLATE, serverUrl, BbUtil.ent(description), - BbUtil.getBlockRelativePath(), BbUtil.ent(title), url, xml, BbUtil.ent(attTitle), - ItemUtil.getIcon(fileForIcon)); - return html; - } - - public static ItemInfo getItemInfo(Content content, Course course, Id folderId, String equellaUrl) { - String body = extractXmlFromBody(content.getBody().getFormattedText()); - final PropBagMin propBag = createPropBag(body); - return parseXml(propBag, content.getId().toExternalString(), course.getId().toExternalString(), - folderId.toExternalString(), equellaUrl); - } - - // Legacy items can have XML that is not always clean. This method covers - // one method of scrubbing the XML and allows future additions as clients - // need them. - protected static PropBagMin createPropBag(String body) { - try { - // Try with the original legacy XML - return new PropBagMin(body); - } catch (final Exception e) { - // That failed. Escape the '&' in the XML and try again - try { - String ampEscapedBody = fixUnescapedAmpersands(body); - return new PropBagMin(ampEscapedBody); - } catch (final Exception e2) { - // Possible future enhancement to escape entities such as   - // or &blahblah; - for now, fail with an error. - LOGGER.logError("Error parsing body of item \n" + body, e2); - throw new RuntimeException(e2); - } - } - } - - // Due to EQ-2260. When &'s are in the item body xml, Equella 6.2+ building - // block fails. Maybe not 100% effective, but should handle the vast - // majority of cases - protected static String fixUnescapedAmpersands(String original) { - final String regex = "&([^;\\W]*([^;\\w]|$))"; - final String replacement = "&$1"; - // The double replaceAll is to handle cases where there are multiple & - // together. - return original.replaceAll(regex, replacement).replaceAll(regex, replacement); - } - - // Due to EQ-2261. Legacy pages are not always ready to be directly called - // and need to be cleaned up - public static String scrubLegacyPage(String original) { - // Due to EQAS-403 / EQ-2306 - if ((original == null) || original.equals("./")) { - return ""; - } - - // When requesting pages that have folder structures in the item, - // don't scrub the path (it should be already clean) - int filenameIndex = original.lastIndexOf("/"); - if (filenameIndex != -1) { - return original; - } - - // There is no folder structure. Scrub the path. Note - Equella doesn't - // seem to like the escape character for space (+) - switch to %20 - return urlEncode(original).replaceAll("\\+", "%20"); - } - - public static String extractXmlFromBody(String body) { - if (body.length() == 0 || !body.startsWith(""); - return body.substring(4, temp); - } - - public static String extractUrlFromBody(String body) { - // This is grotesque. Maybe this should also be recorded in the usage - // table? - final Matcher matcher = PATTERN_URL.matcher(body); - matcher.matches(); - return matcher.group(1); - } - - private static ItemInfo parseXml(PropBagMin xml, String contentId, String courseId, String folderId, - String equellaUrl) { - if (xml.nodeExists("result")) { - xml = xml.getSubtree("result"); - } - if (xml.nodeExists("item") && !xml.getNodeName().equals("item")) { - xml = xml.getSubtree("item"); - } - - final ItemInfo itemInfo = new ItemInfo(equellaUrl, xml.getNode("@id"), xml.getIntNode("@version", 1), contentId, - courseId, folderId, xml.getNode("attachments/@selected")); - - itemInfo.setName(xml.getNode("name")); - itemInfo.setDescription(xml.getNode("description")); - itemInfo.setActivateRequestUuid(xml.getNode("requestUuid", null)); - final String selectedAttachmentType = xml.getNode("attachments/@selectedType"); - if (selectedAttachmentType.equals("link")) { - itemInfo.setMimeType("equella/link"); - } else { - itemInfo.setMimeType("application/octet-stream"); - } - - return itemInfo; - } - - public static String urlEncode(String url) { - try { - return URLEncoder.encode(url, "UTF-8"); - } catch (final UnsupportedEncodingException e) { - // Never happen - return url; - } - } -} \ No newline at end of file diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/PlacementUtil.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/PlacementUtil.java deleted file mode 100644 index 52df8cb..0000000 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/PlacementUtil.java +++ /dev/null @@ -1,167 +0,0 @@ -package org.apereo.openequella.integration.blackboard.common.content; - -import java.net.MalformedURLException; -import java.net.URL; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; - -import blackboard.data.blti.BasicLTIDomainConfig; -import blackboard.data.blti.BasicLTIPlacement; -import blackboard.data.blti.BasicLTIPlacement.Type; -import blackboard.persist.Id; -import blackboard.persist.KeyNotFoundException; -import blackboard.platform.blti.BasicLTIDomainConfigManager; -import blackboard.platform.blti.BasicLTIDomainConfigManagerFactory; -import blackboard.platform.blti.BasicLTIPlacementManager; -import blackboard.platform.plugin.ContentHandler; -import blackboard.platform.plugin.ContentHandlerType.ActionType; - -import com.google.common.base.Throwables; - -import org.apereo.openequella.integration.blackboard.common.BbLogger; -import org.apereo.openequella.integration.blackboard.common.BbUtil; -import org.apereo.openequella.integration.blackboard.common.SqlUtil; -import org.apereo.openequella.integration.blackboard.common.SqlUtil.ResultProcessor; - -/** - * @author Aaron - */ -@SuppressWarnings("nls") -// @NonNullByDefault -public abstract class PlacementUtil { - private static final String LTI_PLACEMENT_TITLE = "openEQUELLA LTI Integration"; - - public PlacementUtil() { - throw new Error(); - } - - public static BasicLTIPlacement getDefaultPlacement() { - return PlacementUtil.loadFromHandle("resource/tle-resource"); - } - - /* @Nullable */ - public static BasicLTIPlacement loadFromHandle(String handle) { - final String sql = "SELECT pk1 FROM blti_placement WHERE handle=?"; - final List ids = SqlUtil.runSql(sql.toString(), new ResultProcessor() { - @Override - public List getResults(ResultSet results) throws SQLException { - final List idList = new ArrayList(); - while (results.next()) { - final Long contentId = results.getLong("pk1"); - final Id id = Id.toId(BasicLTIPlacement.DATA_TYPE, Long.toString(contentId)); - idList.add(id); - } - return idList; - } - }, handle); - if (ids.size() == 0) { - BbLogger.instance().logTrace("No placements found for handle " + handle); - return null; - } - BbLogger.instance().logTrace("Placements found for handle " + handle); - return loadFromId(ids.get(0)); - } - - /* @Nullable */ - public static BasicLTIPlacement loadFromId(Id placementId) { - final BasicLTIPlacementManager basicLTIPlacementManager = BasicLTIPlacementManager.Factory.getInstance(); - try { - return basicLTIPlacementManager.loadById(placementId, false); - } catch (KeyNotFoundException e) { - return null; - } - } - - public static LoadPlacementResponse loadPlacementByUrl(String urlString) throws Exception { - final LoadPlacementResponse response = new LoadPlacementResponse(); - // Handle badly formatted old URLs - final URL url; - try { - url = new URL(urlString); - } catch (MalformedURLException mal) { - return response; - } - - final BasicLTIDomainConfig domainConfig = loadDomainConfigByUrl(url); - response.setDomainConfig(domainConfig); - if (domainConfig != null) { - BbLogger.instance().logTrace("Existing BasicLTIDomainConfig, loading placements"); - final BasicLTIPlacementManager basicLTIPlacementManager = BasicLTIPlacementManager.Factory.getInstance(); - final List placements = basicLTIPlacementManager.loadByDomainConfigId(domainConfig.getId()); - if (placements == null || placements.size() == 0) { - BbLogger.instance().logTrace("No existing BasicLTIPlacements for domain config"); - } else { - // what if more than one? - BbLogger.instance() - .logTrace("Found " + placements.size() + " existing BasicLTIPlacements for host " + url.getHost()); - response.setPlacement(placements.get(0)); - } - } else { - BbLogger.instance().logTrace("No domain config for this URL"); - } - return response; - } - - /* @Nullable */ - public static BasicLTIDomainConfig loadDomainConfigByUrl(URL url) { - final BasicLTIDomainConfigManager fac = BasicLTIDomainConfigManagerFactory.getInstance(); - try { - return fac.loadByDomain(url.getHost()); - } catch (KeyNotFoundException knfUhHuhUhHuh) { - return null; - } - } - - public static BasicLTIPlacement createNewPlacement(BasicLTIDomainConfig domainConfig, ContentHandler contentHandler) { - BbLogger.instance().logTrace("Creating new BasicLTIPlacement"); - try { - final BasicLTIPlacement placement = new BasicLTIPlacement(); - placement.setType(Type.ContentHandler); - placement.setAllowGrading(true); - placement.setContentHandler(contentHandler); - placement.setName(LTI_PLACEMENT_TITLE); - placement.setHandle(contentHandler.getHandle()); - placement.setBasicLTIDomainConfigId(domainConfig.getId()); - return placement; - } catch (Exception e) { - BbLogger.instance().logError("Error creating placement", e); - throw Throwables.propagate(e); - } - } - - public static void deleteById(Id id) { - BbLogger.instance().logTrace("Deleting placement " + id.toExternalString()); - final BasicLTIPlacementManager basicLTIPlacementManager = BasicLTIPlacementManager.Factory.getInstance(); - basicLTIPlacementManager.deleteById(id); - } - - public static void save(BasicLTIPlacement placement) { - BbLogger.instance().logTrace("Saving placement " + placement.getId().toExternalString()); - final BasicLTIPlacementManager basicLTIPlacementManager = BasicLTIPlacementManager.Factory.getInstance(); - basicLTIPlacementManager.saveContentHandlerPlacement(placement, ActionType.none); - } - - // @NonNullByDefault(false) - public static class LoadPlacementResponse { - private BasicLTIPlacement placement; - private BasicLTIDomainConfig domainConfig; - - public BasicLTIPlacement getPlacement() { - return placement; - } - - public void setPlacement(BasicLTIPlacement placement) { - this.placement = placement; - } - - public BasicLTIDomainConfig getDomainConfig() { - return domainConfig; - } - - public void setDomainConfig(BasicLTIDomainConfig domainConfig) { - this.domainConfig = domainConfig; - } - } -} diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/RegistrationUtil.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/RegistrationUtil.java deleted file mode 100644 index 0da1cba..0000000 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/content/RegistrationUtil.java +++ /dev/null @@ -1,749 +0,0 @@ -package org.apereo.openequella.integration.blackboard.common.content; - -import static org.apereo.openequella.integration.blackboard.common.SqlUtil.delete; -import static org.apereo.openequella.integration.blackboard.common.SqlUtil.insert; -import static org.apereo.openequella.integration.blackboard.common.SqlUtil.select; -import static org.apereo.openequella.integration.blackboard.common.SqlUtil.update; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import com.google.common.base.Strings; - -import org.apereo.openequella.integration.blackboard.common.SqlUtil; -import org.apereo.openequella.integration.blackboard.common.SqlUtil.OptionalParam; -import org.apereo.openequella.integration.blackboard.common.SqlUtil.ResultProcessor; -import org.apereo.openequella.integration.blackboard.common.content.ContentUtil.ContentRegisteredResponse; - -/** - * @author Aaron - */ -@SuppressWarnings("nls") -// @NonNullByDefault -public abstract class RegistrationUtil -{ - // until proven otherwise. The databaseVersion is currently 5 (as of after 6.3 GA) - private static int databaseVersion = 0; - - private static final String COL_DATABASE_ID = "ID"; - private static final String COL_INSTITUTION_URL = "INSTURL"; - private static final String COL_DATE_CREATED = "DTCREATED"; - private static final String COL_DATE_MODIFIED = "DTMODIFIED"; - private static final String COL_UUID = "UUID"; - private static final String COL_VERSION = "VERSION"; - private static final String COL_PAGE = "PAGE"; - private static final String COL_CONTENT_ID = "CONTENTID"; - private static final String COL_BLACKBOARD_TITLE = "BBTITLE"; - private static final String COL_BLACKBOARD_DESC = "BBDESC"; - private static final String COL_COURSE_NAME = "COURSENAME"; - private static final String COL_COURSE_ID = "COURSEID"; - private static final String COL_FOLDER_ID = "FOLDERID"; - private static final String COL_AVAILABLE = "AVAILABLE"; - private static final String COL_COURSE_AVAILABLE = "COURSEAVAILABLE"; - private static final String COL_DATE_ACCESSED = "DTACCESSED"; - private static final String COL_ATTACHMENT_NAME = "ATTNAME"; - - public RegistrationUtil() - { - throw new Error(); - } - - public static void recordItem(ItemKey itemKey, boolean added, boolean available, boolean courseAvailable, - String bbTitle, String bbDesc, String attachmentName, Date dateAdded, Date dateModifed, String courseName) - { - //BbUtil.trace("recordItem(" + itemKey + ", " + added + ", " + available + ", " + courseAvailable + ", " + bbTitle - // + ", " + bbDesc + ", " + attachmentName + ", " + dateAdded + ", " + dateModifed + ", " + courseName + ")"); - ensureDatabase(); - - StringBuilder sql = null; - - final SqlUtil.DbType t = SqlUtil.getDatabaseType(); - final String schema = SqlUtil.schema(); - - final long now = System.currentTimeMillis(); - final java.sql.Timestamp addedTimestamp = new java.sql.Timestamp(dateAdded == null ? now : dateAdded.getTime()); - final java.sql.Timestamp modifiedTimestamp = new java.sql.Timestamp( - dateModifed == null ? now : dateModifed.getTime()); - - if( added ) - { - if( t == SqlUtil.DbType.ORACLE ) - { - sql = insert(COL_DATABASE_ID, COL_DATE_CREATED, COL_DATE_MODIFIED, COL_INSTITUTION_URL, COL_UUID, - COL_VERSION, COL_COURSE_ID, COL_FOLDER_ID, COL_AVAILABLE, COL_COURSE_AVAILABLE, - COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC, COL_PAGE, COL_CONTENT_ID, COL_COURSE_NAME, - COL_ATTACHMENT_NAME).append(" VALUES (").append(schema) - .append("equellaseq.nextval, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ,?, ?, ?, ?)"); - } - else if( t == SqlUtil.DbType.SQL_SERVER ) - { - sql = insert(COL_DATE_CREATED, COL_DATE_MODIFIED, COL_INSTITUTION_URL, COL_UUID, COL_VERSION, - COL_COURSE_ID, COL_FOLDER_ID, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, - COL_BLACKBOARD_DESC, COL_PAGE, COL_CONTENT_ID, COL_COURSE_NAME, COL_ATTACHMENT_NAME) - .append(" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); - } - else if( t == SqlUtil.DbType.POSTGRES ) - { - sql = insert(COL_DATABASE_ID, COL_DATE_CREATED, COL_DATE_MODIFIED, COL_INSTITUTION_URL, COL_UUID, - COL_VERSION, COL_COURSE_ID, COL_FOLDER_ID, COL_AVAILABLE, COL_COURSE_AVAILABLE, - COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC, COL_PAGE, COL_CONTENT_ID, COL_COURSE_NAME, - COL_ATTACHMENT_NAME) - .append(" VALUES (nextval('equellaseq'), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); - } - - SqlUtil.runSql(sql.toString(), null, addedTimestamp, modifiedTimestamp, itemKey.getInstitutionUrl(), - itemKey.getUuid(), itemKey.getVersion(), itemKey.getCourseId(), itemKey.getFolderId(), available, - courseAvailable, bbTitle, trim(bbDesc, 511), itemKey.getPage(), itemKey.getContentId(), courseName, - attachmentName); - itemKey.setDatabaseId(0); // would be nice to know what the new one - // is - } - else - { - String contentId = itemKey.getContentId(); - if( !Strings.isNullOrEmpty(contentId) ) - { - //BbUtil.debug("updating recorded usage using contentId"); - - sql = update(COL_DATE_MODIFIED, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, - COL_BLACKBOARD_DESC, COL_PAGE, COL_COURSE_NAME, COL_ATTACHMENT_NAME).append("WHERE CONTENTID=?"); - List updateCount = SqlUtil.runSql(sql.toString(), null, modifiedTimestamp, available, - courseAvailable, bbTitle, bbDesc, itemKey.getPage(), courseName, attachmentName, contentId); - - int updates = updateCount.get(0); - if( updates > 1 ) - { - // There have instances of dupes. Remove them all. This - // should be a rare occurrence - //BbUtil.error("** duplicate recordings of " + contentId + " **", null); - - // remove dupes and add a brand new entry - unrecordItem(0, contentId); - recordItem(itemKey, true, available, courseAvailable, bbTitle, bbDesc, attachmentName, dateAdded, - dateModifed, courseName); - } - else if( updates == 0 ) - { - //BbUtil.debug("content ID not logged... updating the existing one and record the content ID"); - - sql = update(COL_DATE_MODIFIED, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, - COL_BLACKBOARD_DESC, COL_PAGE, COL_COURSE_NAME, COL_CONTENT_ID, COL_ATTACHMENT_NAME) - .append("WHERE INSTURL=? AND UUID=? AND VERSION=? AND COURSEID=? AND FOLDERID=?"); - updateCount = SqlUtil.runSql(sql.toString(), null, modifiedTimestamp, available, courseAvailable, - bbTitle, trim(bbDesc, 511), itemKey.getPage(), courseName, itemKey.getContentId(), - attachmentName, itemKey.getInstitutionUrl(), itemKey.getUuid(), itemKey.getVersion(), - itemKey.getCourseId(), itemKey.getFolderId()); - checkSingleUpdate(updateCount); - } - } - else - { - //BbUtil.debug("updating recorded usage using old field matching"); - - sql = update(COL_DATE_MODIFIED, COL_AVAILABLE, COL_COURSE_AVAILABLE, COL_BLACKBOARD_TITLE, - COL_BLACKBOARD_DESC, COL_PAGE, COL_COURSE_NAME, COL_ATTACHMENT_NAME) - .append("WHERE INSTURL=? AND UUID=? AND VERSION=? AND COURSEID=? AND FOLDERID=?"); - final List updateCount = SqlUtil.runSql(sql.toString(), null, modifiedTimestamp, available, - courseAvailable, bbTitle, trim(bbDesc, 511), itemKey.getPage(), courseName, attachmentName, - itemKey.getInstitutionUrl(), itemKey.getUuid(), itemKey.getVersion(), itemKey.getCourseId(), - itemKey.getFolderId()); - checkSingleUpdate(updateCount); - } - } - } - - private static boolean checkSingleUpdate(List updateCount) - { - final int updates = updateCount.get(0); - if( updates != 1 ) - { - // houston, we have a problem - //BbUtil.error("** Updated " + updates + " rows when only one should have been updated **", null); - return false; - } - return true; - } - - private static boolean unrecordItem(int usageId) - { - //BbUtil.debug("unrecordItem(" + usageId + ")"); - ensureDatabase(); - - final List updateCount = SqlUtil.runSql(delete() + "WHERE ID=?", null, usageId); - return updateCount.get(0) > 0; - } - - public static boolean unrecordItem(int usageId, String contentId) - { - //BbUtil.debug("unrecordItem(" + usageId + ", " + contentId + ")"); - ensureDatabase(); - - if( usageId != 0 ) - { - boolean unrecorded = unrecordItem(usageId); - if( unrecorded ) - { - return true; - } - } - - final List updateCount = SqlUtil.runSql(delete() + "WHERE CONTENTID=?", null, contentId); - return updateCount.get(0) > 0; - } - - public static boolean updateRecordedTitle(String contentId, String title, String description) - { - //BbUtil.debug("updateRecordedTitle(" + contentId + ", " + title + ", " + description + ")"); - ensureDatabase(); - - final List updateCount = SqlUtil.runSql( - update(COL_BLACKBOARD_TITLE, COL_BLACKBOARD_DESC).append("WHERE CONTENTID=?").toString(), null, title, - description, contentId); - return updateCount.get(0) > 0; - } - - public static boolean updateRecordedLocation(String contentId, String courseId, String folderId) - { - //BbUtil.debug("updateRecordedLocation(" + contentId + ", " + courseId + ", " + folderId + ")"); - ensureDatabase(); - - final List updateCount = SqlUtil.runSql( - update(COL_COURSE_ID, COL_FOLDER_ID).append("WHERE CONTENTID=?").toString(), null, courseId, folderId, - contentId); - return updateCount.get(0) > 0; - } - - public static boolean updateDateAccessed(String contentId, Date dateAccessed) - { - //BbUtil.debug("updateDateAccessed(" + contentId + ", " + dateAccessed + ")"); - ensureDatabase(); - - final Timestamp accessedTimestamp = new Timestamp(dateAccessed.getTime()); - final List updateCount = SqlUtil.runSql( - update(COL_DATE_ACCESSED).append("WHERE CONTENTID=?").toString(), null, accessedTimestamp, contentId); - return updateCount.get(0) > 0; - } - - public static List findUsages(final String institutionUrl, final String itemUuid, final int itemVersion, - boolean versionIsLatest, boolean allVersions, boolean available) - { - //BbUtil.debug("findUsages(" + institutionUrl + ", " + itemUuid + ", " + itemVersion + ", " + allVersions + ", " - // + available + ")"); - ensureDatabase(); - - final StringBuilder sql = select().append("WHERE INSTURL=? AND UUID=?"); - if( !allVersions ) - { - sql.append(" AND (VERSION=?"); - if( versionIsLatest ) - { - sql.append(" OR VERSION=0"); - } - sql.append(")"); - } - if( available ) - { - sql.append(" AND AVAILABLE=? AND COURSEAVAILABLE=?"); - } - sql.append(" ORDER BY DTCREATED DESC"); - - final List usages = SqlUtil.runSql(sql.toString(), new ResultProcessor() - { - @Override - public List getResults(ResultSet results) throws SQLException - { - final List u = new ArrayList(); - while( results.next() ) - { - final String contentId = results.getString(COL_CONTENT_ID); - final ItemInfo r = new ItemInfo(institutionUrl, itemUuid, results.getInt(COL_VERSION), contentId, - results.getString(COL_COURSE_ID), results.getString(COL_FOLDER_ID), - results.getString(COL_PAGE)); - r.setCreatedDate(results.getTimestamp(COL_DATE_CREATED)); - r.setModifiedDate(results.getTimestamp(COL_DATE_MODIFIED)); - r.setAvailable(results.getBoolean(COL_AVAILABLE)); - r.setName(results.getString(COL_BLACKBOARD_TITLE)); - r.setDescription(results.getString(COL_BLACKBOARD_DESC)); - r.getItemKey().setDatabaseId(results.getInt(COL_DATABASE_ID)); - r.setAttachmentName(results.getString(COL_ATTACHMENT_NAME)); - u.add(r); - } - return u; - } - }, institutionUrl, itemUuid, new OptionalParam(itemVersion, !allVersions), - new OptionalParam(available, available), new OptionalParam(available, available)); - return usages; - } - - public static List findAllUsages(final String institutionUrl, final String query, final String courseId, - String folderId, boolean available, String sortColumn, boolean sortReverse) - { - //BbUtil.debug("findAllUsages(" + institutionUrl + ", " + query + ", " + courseId + ", " + folderId + ", " - // + available + ", " + sortColumn + ", " + sortReverse + ")"); - ensureDatabase(); - - final StringBuilder sql = select().append(" WHERE INSTURL=?"); - - final boolean hasQuery = !Strings.isNullOrEmpty(query); - String likeQuery = null; - if( hasQuery ) - { - likeQuery = '%' + query.toLowerCase() + '%'; - sql.append(" AND LOWER(BBTITLE) LIKE ?"); - } - final boolean hasCourseId = !Strings.isNullOrEmpty(courseId); - if( hasCourseId ) - { - sql.append(" AND COURSEID = ?"); - } - final boolean hasFolderId = !Strings.isNullOrEmpty(folderId); - if( hasFolderId ) - { - sql.append(" AND FOLDERID = ?"); - } - if( available ) - { - sql.append(" AND AVAILABLE=? AND COURSEAVAILABLE=?"); - } - - String sortCol = "DTCREATED"; - String sortOrd = sortReverse ? "DESC" : "ASC"; - if( !Strings.isNullOrEmpty(sortColumn) ) - { - if( sortColumn.equals("name") ) - { - sortCol = "LOWER(BBTITLE)"; - } - else if( sortColumn.equals("course") ) - { - sortCol = "LOWER(COURSENAME)"; - } - } - sql.append(" ORDER BY " + sortCol + " " + sortOrd); - - //@formatter:off - final List usages = SqlUtil.runSql( - sql.toString(), - new ResultProcessor() - { - @Override - public List getResults(ResultSet results) throws SQLException - { - final List u = new ArrayList(); - while( results.next() ) - { - final ItemInfo r = new ItemInfo( - institutionUrl, - results.getString(COL_UUID), - results.getInt(COL_VERSION), - results.getString(COL_CONTENT_ID), - results.getString(COL_COURSE_ID), - results.getString(COL_FOLDER_ID), - results.getString(COL_PAGE) - ); - r.setCreatedDate(results.getTimestamp(COL_DATE_CREATED)); - r.setModifiedDate(results.getTimestamp(COL_DATE_MODIFIED)); - r.setAvailable(results.getBoolean(COL_AVAILABLE)); - r.setName(results.getString(COL_BLACKBOARD_TITLE)); - r.setDescription(results.getString(COL_BLACKBOARD_DESC)); - r.getItemKey().setDatabaseId(results.getInt(COL_DATABASE_ID)); - r.setDateAccessed(results.getTimestamp(COL_DATE_ACCESSED)); - r.setAttachmentName(results.getString(COL_ATTACHMENT_NAME)); - u.add(r); - } - return u; - } - }, - institutionUrl, - new OptionalParam(likeQuery, hasQuery), - new OptionalParam(courseId, hasCourseId), - new OptionalParam(folderId, hasFolderId), - new OptionalParam(available, available), - new OptionalParam(available, available) - ); - //@formatter:on - return usages; - } - - // Used by SynchroniseContentThread - public static List findEquellaContentByCourse(final String institutionUrl, final String courseId, - boolean available) - { - //BbUtil.debug("findEquellaContentByCourse(" + institutionUrl + ", " + courseId + ", " + available + ")"); - ensureDatabase(); - - // ID, UUID, VERSION, FOLDERID, ATTACHMENT_NAME, CONTENTID - final StringBuilder sql = select(COL_DATABASE_ID, COL_UUID, COL_VERSION, COL_FOLDER_ID, COL_PAGE, - COL_CONTENT_ID).append("WHERE INSTURL=? AND COURSEID=?"); - if( available ) - { - sql.append(" AND AVAILABLE=? AND COURSEAVAILABLE=?"); - } - - final List usages = SqlUtil.runSql(sql.toString(), new ResultProcessor() - { - @Override - public List getResults(ResultSet results) throws SQLException - { - List u = new ArrayList(); - while( results.next() ) - { - final int dbId = results.getInt(COL_DATABASE_ID); - final String uuid = results.getString(COL_UUID); - final int version = results.getInt(COL_VERSION); - final String folderId = results.getString(COL_FOLDER_ID); - final String page = results.getString(COL_PAGE); - final String contentId = results.getString(COL_CONTENT_ID); - - final ItemKey k = new ItemKey(institutionUrl, uuid, version, contentId, courseId, folderId, page); - k.setDatabaseId(dbId); - u.add(k); - } - return u; - } - }, institutionUrl, courseId, new OptionalParam(available, available), - new OptionalParam(available, available)); - return usages; - } - - public static int cleanupBadContent(final String institutionUrl) - { - //BbUtil.debug("cleanupBadContent(" + institutionUrl + ")"); - ensureDatabase(); - - String sql = delete() + "WHERE INSTURL=? AND CONTENTID IS NULL"; - List updates = SqlUtil.runSql(sql, null, institutionUrl); - return updates.get(0); - } - - public static ContentRegisteredResponse contentIsRegistered(ItemKey itemKey) - { - //BbUtil.debug("contentIsRegistered(" + itemKey + ")"); - ensureDatabase(); - - final ResultProcessor resultProcessor = new ResultProcessor() - { - @Override - public List getResults(ResultSet results) throws SQLException - { - List r = new ArrayList(); - while( results.next() ) - { - r.add(new Object[]{results.getInt(COL_DATABASE_ID), results.getBoolean(COL_AVAILABLE), - results.getString(COL_CONTENT_ID)}); - } - return r; - } - }; - - final List res; - final StringBuilder sql = select(COL_DATABASE_ID, COL_AVAILABLE, COL_CONTENT_ID); - if( itemKey.getContentId() != null ) - { - sql.append("WHERE CONTENTID=?"); - res = SqlUtil.runSql(sql.toString(), resultProcessor, itemKey.getContentId()); - } - else - { - // OLD STYLE - final String page = itemKey.getPage(); - sql.append("WHERE INSTURL=? AND UUID=? AND VERSION=? AND COURSEID=? AND FOLDERID=? AND PAGE" - + (Strings.isNullOrEmpty(page) ? " IS NULL" : "=?")); - - res = SqlUtil.runSql(sql.toString(), resultProcessor, itemKey.getInstitutionUrl(), itemKey.getUuid(), - itemKey.getVersion(), itemKey.getCourseId(), itemKey.getFolderId(), - new OptionalParam(page, !Strings.isNullOrEmpty(page))); - } - - final ContentRegisteredResponse response = new ContentRegisteredResponse(); - if( !res.isEmpty() ) - { - response.setRegistered(true); - final Object[] r = res.get(0); - response.setId((Integer) r[0]); - response.setAvailable((Boolean) r[1]); - response.setContentId((String) r[2]); - } - - return response; - } - - private static void ensureDatabase() - { - if( databaseVersion < 1 ) - { - try - { - SqlUtil.runSql(SqlUtil.select("1").toString(), null); - databaseVersion = 1; - } - catch( Exception e ) - { - makeDatabase(); - databaseVersion = 1; - } - } - - if( databaseVersion < 2 ) - { - if( !SqlUtil.columnExists(COL_CONTENT_ID) ) - { - modifyDatabaseAddContentAndCourseName(); - } - databaseVersion = 2; - } - - if( databaseVersion < 3 ) - { - if( !SqlUtil.columnExists(COL_BLACKBOARD_DESC) ) - { - modifyDatabaseAddDescription(); - } - databaseVersion = 3; - } - - if( databaseVersion < 4 ) - { - if( !SqlUtil.columnExists(COL_DATE_ACCESSED) ) - { - modifyDatabaseAddDateAccessed(); - } - databaseVersion = 4; - } - - if( databaseVersion < 5 ) - { - if( !SqlUtil.columnExists(COL_ATTACHMENT_NAME) ) - { - modifyDatabaseAddAttachmentName(); - } - databaseVersion = 5; - } - } - - /** - * Todo: this clearly isn't normalised. But the question is, is it worth it? - */ - private static void makeDatabase() - { - final SqlUtil.DbType t = SqlUtil.getDatabaseType(); - if( t == SqlUtil.DbType.ORACLE ) - { - //BbUtil.debug("Oracle DB detected"); - - SqlUtil.runSql("CREATE SEQUENCE equellaseq START WITH 1 INCREMENT BY 1 NOMAXVALUE", null); - - //@formatter:off - final String sql = "CREATE TABLE equellacontent (" - + "ID NUMBER NOT NULL, " - + "INSTURL NVARCHAR2(255) NOT NULL, " - + "DTCREATED DATE DEFAULT SYSDATE, " - + "DTMODIFIED DATE DEFAULT SYSDATE, " - + "BBTITLE NVARCHAR2(1024) NOT NULL," - + "PAGE NVARCHAR2(1024)," //nullable for item summaries - + "UUID NVARCHAR2(40) NOT NULL, " - + "VERSION NUMBER NOT NULL," - + "COURSEID NVARCHAR2(32) NOT NULL, " - + "FOLDERID NVARCHAR2(32) NOT NULL, " - + "AVAILABLE NUMBER NOT NULL," - + "COURSEAVAILABLE NUMBER NOT NULL," - + "CONSTRAINT equellacontent_pk PRIMARY KEY (ID))"; - //@formatter:on - SqlUtil.runSql(sql, null); - - SqlUtil.runSql("CREATE INDEX eqcontent_uuidversion ON equellacontent(INSTURL, UUID, VERSION)", null); - // cannot be UNIQUE since same item could be added to same folder - // (in theory) - SqlUtil.runSql("CREATE INDEX eqcontent_all ON equellacontent(INSTURL, UUID, VERSION, COURSEID, FOLDERID)", - null); - SqlUtil.runSql("CREATE INDEX eqcontent_course ON equellacontent(INSTURL, COURSEID)", null); - SqlUtil.runSql("CREATE INDEX eqcontent_available ON equellacontent(AVAILABLE, COURSEAVAILABLE)", null); - } - else if( t == SqlUtil.DbType.SQL_SERVER ) - { - //BbUtil.debug("SQL Server DB detected"); - - //@formatter:off - final String sql = "CREATE TABLE dbo.equellacontent (" - + "ID int NOT NULL PRIMARY KEY IDENTITY, " - + "INSTURL nvarchar(255) NOT NULL, " - + "BBTITLE nvarchar(1024) NOT NULL, " - + "PAGE nvarchar(1024) NULL, " //nullable for item summaries - + "DTCREATED datetime NOT NULL, " - + "DTMODIFIED datetime NOT NULL, " - + "UUID nvarchar(40) NOT NULL, " - + "VERSION int NOT NULL," - + "COURSEID nvarchar(32) NOT NULL, " - + "FOLDERID nvarchar(32) NOT NULL, " - + "AVAILABLE bit NOT NULL," - + "COURSEAVAILABLE bit NOT NULL" - + ")"; - //@formatter:on - SqlUtil.runSql(sql, null); - - SqlUtil.runSql("CREATE INDEX eqcontent_course ON dbo.equellacontent(INSTURL, COURSEID)", null); - // cannot be UNIQUE since same item could be added to same folder - // (in theory) - SqlUtil.runSql( - "CREATE INDEX eqcontent_all ON dbo.equellacontent(INSTURL, UUID, VERSION, COURSEID, FOLDERID)", null); - SqlUtil.runSql("CREATE INDEX eqcontent_uuidversion ON dbo.equellacontent(INSTURL, UUID, VERSION)", null); - SqlUtil.runSql("CREATE INDEX eqcontent_available ON dbo.equellacontent(AVAILABLE, COURSEAVAILABLE)", null); - } - else if( t == SqlUtil.DbType.POSTGRES ) - { - //BbUtil.debug("Postgres DB detected"); - - try - { - SqlUtil.runSql("CREATE SEQUENCE equellaseq START WITH 1 INCREMENT BY 1 NO MAXVALUE", null); - } - catch( Exception e ) - { - // Ignore, it may already exist - } - - //@formatter:off - final String sql = "CREATE TABLE equellacontent (" - + "ID int NOT NULL PRIMARY KEY, " - + "INSTURL varchar(255) NOT NULL, " - + "BBTITLE varchar(1024) NOT NULL, " - + "PAGE varchar(1024) NULL, " //nullable for item summaries - + "DTCREATED timestamp without time zone NOT NULL, " - + "DTMODIFIED timestamp without time zone NOT NULL, " - + "UUID varchar(40) NOT NULL, " - + "VERSION int NOT NULL," - + "COURSEID varchar(32) NOT NULL, " - + "FOLDERID varchar(32) NOT NULL, " - + "AVAILABLE boolean NOT NULL," - + "COURSEAVAILABLE boolean NOT NULL, " - + "CONTENTID varchar(32) NULL, " - + "COURSENAME varchar(512) NULL, " - + "BBDESC varchar(512) NULL, " - + "DTACCESSED timestamp without time zone NULL, " - + "ATTNAME varchar(512) NULL" - + ")"; - //@formatter:on - SqlUtil.runSql(sql, null); - - SqlUtil.runSql("CREATE INDEX eqcontent_course ON equellacontent(INSTURL, COURSEID)", null); - // cannot be UNIQUE since same item could be added to same folder - // (in theory) - SqlUtil.runSql("CREATE INDEX eqcontent_all ON equellacontent(INSTURL, UUID, VERSION, COURSEID, FOLDERID)", - null); - SqlUtil.runSql("CREATE INDEX eqcontent_uuidversion ON equellacontent(INSTURL, UUID, VERSION)", null); - SqlUtil.runSql("CREATE INDEX eqcontent_available ON equellacontent(AVAILABLE, COURSEAVAILABLE)", null); - SqlUtil.runSql("CREATE INDEX eqcontent_content ON equellacontent(INSTURL, CONTENTID)", null); - } - } - - private static void modifyDatabaseAddContentAndCourseName() - { - final SqlUtil.DbType t = SqlUtil.getDatabaseType(); - if( t == SqlUtil.DbType.ORACLE ) - { - // debug("Oracle DB detected"); - //@formatter:off - final String sql = "ALTER TABLE equellacontent ADD (" - + "CONTENTID NVARCHAR2(32) NULL, " - + "COURSENAME NVARCHAR2(512) NULL" - + ")"; - //@formatter:on - SqlUtil.runSql(sql, null); - SqlUtil.runSql("CREATE INDEX eqcontent_content ON equellacontent(INSTURL, CONTENTID)", null); - } - else if( t == SqlUtil.DbType.SQL_SERVER ) - { - // debug("SQL Server DB detected"); - //@formatter:off - final String sql = "ALTER TABLE dbo.equellacontent ADD " - + "CONTENTID nvarchar(32) NULL, " - + "COURSENAME nvarchar(512) NULL"; - //@formatter:on - SqlUtil.runSql(sql, null); - SqlUtil.runSql("CREATE INDEX eqcontent_content ON dbo.equellacontent(INSTURL, CONTENTID)", null); - } - else if( t == SqlUtil.DbType.POSTGRES ) - { - // N/A. There won't be any Postgres DBs this old - } - } - - private static void modifyDatabaseAddDescription() - { - final SqlUtil.DbType t = SqlUtil.getDatabaseType(); - String sql = null; - if( t == SqlUtil.DbType.ORACLE ) - { - sql = "ALTER TABLE equellacontent ADD (BBDESC NVARCHAR2(512) NULL)"; - } - else if( t == SqlUtil.DbType.SQL_SERVER ) - { - sql = "ALTER TABLE dbo.equellacontent ADD BBDESC nvarchar(512) NULL"; - } - else if( t == SqlUtil.DbType.POSTGRES ) - { - // N/A. There won't be any Postgres DBs this old - } - if( sql != null ) - { - SqlUtil.runSql(sql, null); - } - } - - private static void modifyDatabaseAddDateAccessed() - { - final SqlUtil.DbType t = SqlUtil.getDatabaseType(); - String sql = null; - if( t == SqlUtil.DbType.ORACLE ) - { - sql = "ALTER TABLE equellacontent ADD (" + COL_DATE_ACCESSED + " DATE NULL)"; - } - else if( t == SqlUtil.DbType.SQL_SERVER ) - { - sql = "ALTER TABLE dbo.equellacontent ADD " + COL_DATE_ACCESSED + " datetime NULL"; - } - else if( t == SqlUtil.DbType.POSTGRES ) - { - // N/A. There won't be any Postgres DBs this old - } - if( sql != null ) - { - SqlUtil.runSql(sql, null); - } - } - - private static void modifyDatabaseAddAttachmentName() - { - final SqlUtil.DbType t = SqlUtil.getDatabaseType(); - String sql = null; - if( t == SqlUtil.DbType.ORACLE ) - { - sql = "ALTER TABLE equellacontent ADD (" + COL_ATTACHMENT_NAME + " NVARCHAR2(512) NULL)"; - } - else if( t == SqlUtil.DbType.SQL_SERVER ) - { - sql = "ALTER TABLE dbo.equellacontent ADD " + COL_ATTACHMENT_NAME + " nvarchar(512) NULL"; - } - else if( t == SqlUtil.DbType.POSTGRES ) - { - // N/A. There won't be any Postgres DBs this old - } - if( sql != null ) - { - SqlUtil.runSql(sql, null); - } - } - - private static String trim(String text, int maxlen) - { - if( text != null && text.length() > maxlen ) - { - return text.substring(0, maxlen); - } - return text; - } -} diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/BadCharacterFilterReader.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/BadCharacterFilterReader.java deleted file mode 100644 index fce0ace..0000000 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/BadCharacterFilterReader.java +++ /dev/null @@ -1,237 +0,0 @@ -package org.apereo.openequella.integration.blackboard.common.propbag; - -import java.io.FilterReader; -import java.io.IOException; -import java.io.Reader; - -/** - * keeps reading until we get a '&' and then buffer the rest as 'suspect' - * until a ';' is read. Only then can we determine if the suspect buffer is - * illegal and can be discarded, OR it's okay and we can 'read' it in. If we - * read in an illegal unicode char, it can be discarded straight away. This code - * is deliberately mad-hacks (eg unrolled loops and inline method code) for - * performance reasons - * - * @author aholland - */ -public class BadCharacterFilterReader extends FilterReader { - private static final int DEFAULT_CHUNK = 1000; - private static final int MAX_SUSPECTION_LENGTH = 10; - - protected final char[] chunkBuff; - protected final char[] suspect = new char[MAX_SUSPECTION_LENGTH]; - - protected int suspectStart; - protected int suspectLength; - - protected boolean didDiscard; - - public BadCharacterFilterReader(final Reader reader) { - this(reader, DEFAULT_CHUNK); - } - - public BadCharacterFilterReader(final Reader reader, final int bufferSize) { - super(reader); - if (bufferSize <= MAX_SUSPECTION_LENGTH) { - throw new IllegalArgumentException("bufferSize must be at least " + MAX_SUSPECTION_LENGTH); //$NON-NLS-1$ - } - chunkBuff = new char[bufferSize]; - - // I assume the super (Reader) constructor initialises its lock Object? - // But just in case, affirm it here. - if (lock == null) { - lock = new Object(); - } - } - - @Override - public int read() { - throw new UnsupportedOperationException(); - } - - @Override - public int read(final char[] outBuffer, final int originalWriteOffset, final int len) throws IOException { - synchronized (lock) { - if (in == null) { - throw new IOException("Stream closed"); //$NON-NLS-1$ - } - - int writeOffset = originalWriteOffset; - final int maxWriteSize = (chunkBuff.length < len ? chunkBuff.length : len); - - // always flush the suspect buffer it needs to be. - // suspectStart > 0 means we have already started writing it - if (suspectStart > 0) { - final int slen = (suspectLength < maxWriteSize ? suspectLength : maxWriteSize); - return writeSuspect(outBuffer, originalWriteOffset, slen); - } else { - // note that we may be writing out a suspect buffer filled from - // a previous call - // so we cannot always read in the full allowed amount - final int maxWrappedReadSize = (maxWriteSize > suspectLength ? maxWriteSize - suspectLength : 0); - final int wrappedReadSize = in.read(chunkBuff, 0, maxWrappedReadSize); - - // not end of stream? - if (wrappedReadSize >= 0) { - for (int i = 0; i < wrappedReadSize; ++i) { - final int next = chunkBuff[i]; - - // invalid unicode char - if ((next < 0x20 && next != 0x9 && next != 0xA && next != 0xD) || (next > 0xD7FF && next < 0xE000) - || (next > 0xFFFD && next < 0x10000) || next > 0x10FFFF) { - // discard - didDiscard = true; - } else { - // are we suspicious? - if (suspectLength > 0) { - // is it a ';'? - if (next == ';') { - suspect[suspectStart + suspectLength] = (char) next; - ++suspectLength; - if (suspectLength == MAX_SUSPECTION_LENGTH) { - // it clearly isn't a suspect buffer - writeOffset += writeSuspect(outBuffer, writeOffset, len - writeOffset); - } else { - // so, is it dodgy? buffer at this point - // could be #x999, #99, amp or anything! - if (isDodgy()) { - // it IS dodgy. clear out suspect - // buffer - suspectStart = 0; - suspectLength = 0; - didDiscard = true; - } else { - writeOffset += writeSuspect(outBuffer, writeOffset, len - writeOffset); - } - } - } else { - suspect[suspectStart + suspectLength] = (char) next; - ++suspectLength; - if (suspectStart + suspectLength == MAX_SUSPECTION_LENGTH) { - // it clearly isn't a suspect buffer - writeOffset += writeSuspect(outBuffer, writeOffset, len - writeOffset); - } - } - } else { - // seems ok, is it a '&'? - if (next == '&') { - // start pumping the suspect buffer - suspect[suspectStart + suspectLength] = '&'; - ++suspectLength; - } else { - outBuffer[writeOffset] = (char) next; - ++writeOffset; - } - } - } - } - - return writeOffset - originalWriteOffset; - } - - // nothing left to read, do we have a suspect buffer to clear? - else { - if (suspectLength > 0) { - final int slen = (suspectLength < maxWriteSize ? suspectLength : maxWriteSize); - return writeSuspect(outBuffer, originalWriteOffset, slen); - } - - // this is it. it's all over - return -1; - } - } - } - } - - public boolean didDiscard() { - return didDiscard; - } - - @SuppressWarnings("nls") - protected int writeSuspect(final char[] output, final int position, final int maxLength) { - final int len = (suspectLength < maxLength ? suspectLength : maxLength); - if (suspect.length < len + suspectStart || output.length < len + position || len < 0) { - throw new ArrayIndexOutOfBoundsException( - "suspect buffer size: " + suspect.length + ", output buffer size: " + output.length + ", suspect start: " - + suspectStart + ", position: " + position + ", maxLength: " + maxLength); - } - System.arraycopy(suspect, suspectStart, output, position, len); - - // fully written buffer - if ((len == suspectLength)) { - suspectStart = 0; - suspectLength = 0; - } else { - suspectStart += len; - suspectLength -= len; - } - - return len; - } - - /** - * A very apt name for this code methinks... Preconditions: suspectStart is - * ALWAYS zero here suspect[0] == '&' ALWAYS suspect[suspectLength-1] == ';' - * ALWAYS - * - * @param suspect - * @param suspectLength - * @return - */ - protected boolean isDodgy() { - // if it starts with hash -> then it's a numbered escape sequence - if (suspect[1] == '#') { - if (suspectLength > 2) { - // is the next an x or a number? - final char c3 = suspect[2]; - if (c3 == 'x') { - if (suspectLength > 3) { - // at this point, bugger optimisations it's looking a - // fair chance - // bear in mind the suspect will always end in ';' at - // this point, hence the -4 - final String str = new String(suspect, 3, suspectLength - 4); - // is this a hex representation of an invalid range? - try { - final int i = Integer.parseInt(str, 16); - if ((i < 0x20 && i != 0x9 && i != 0xA && i != 0xD) || (i > 0xD7FF && i < 0xE000) - || (i > 0xFFFD && i < 0x10000) || i > 0x10FFFF) { - return true; - } - } catch (final NumberFormatException n) { - // nope - } - } - } else if (c3 == '0' || c3 == '1' || c3 == '2' || c3 == '3' || c3 == '4' || c3 == '5' || c3 == '6' || c3 == '7' - || c3 == '8' || c3 == '9') { - // at this point, bugger optimisations it's looking a fair - // chance - // bear in mind the suspect will always end in ';' at this - // point, hence the -3 - final String str = new String(suspect, 2, suspectLength - 3); - // is this a decimal representation of an invalid range? - try { - final int i = Integer.parseInt(str, 10); - if ((i < 0x20 && i != 0x9 && i != 0xA && i != 0xD) || (i > 0xD7FF && i < 0xE000) - || (i > 0xFFFD && i < 0x10000) || i > 0x10FFFF) { - return true; - } - } catch (final NumberFormatException n) { - // nope - } - } - } - } - return false; - } - - @Override - public void close() throws IOException { - synchronized (lock) { - if (in != null) { - in.close(); - in = null; - } - } - } -} diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/BadCharacterFilterWriter.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/BadCharacterFilterWriter.java deleted file mode 100644 index e5f955e..0000000 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/BadCharacterFilterWriter.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.apereo.openequella.integration.blackboard.common.propbag; - -import java.io.FilterWriter; -import java.io.IOException; -import java.io.Writer; - -/** - * Can only strip out invalid unicode chars as we see them... this means that - * escaped invalid chars will slip through the cracks. Eg &#x0B; Not as big - * a deal as it seems, when WE read the chars back in, they will be removed. If - * anyone else reads them in... unrucky. - * - * @author aholland - */ -public class BadCharacterFilterWriter extends FilterWriter { - protected boolean didDiscard; - private final Writer wrapped; - - public BadCharacterFilterWriter(final Writer writer) { - super(writer); - wrapped = writer; - } - - @Override - public void write(final String str) throws IOException { - write(str.toCharArray()); - } - - @Override - public void write(final int c) throws IOException { - // invalid unicode char - if ((c < 0x20 && c != 0x9 && c != 0xA && c != 0xD) || (c > 0xD7FF && c < 0xE000) || (c > 0xFFFD && c < 0x10000) - || c > 0x10FFFF) { - // discard - didDiscard = true; - } else { - wrapped.write(c); - } - } - - @Override - public void write(final char[] cbuf, final int off, final int len) throws IOException { - int writeLength = 0; - final char[] buff = new char[len]; - for (int i = off; i < off + len; ++i) { - final char c = cbuf[i]; - // invalid unicode char - if ((c < 0x20 && c != 0x9 && c != 0xA && c != 0xD) || (c > 0xD7FF && c < 0xE000) || (c > 0xFFFD && c < 0x10000) - || c > 0x10FFFF) { - // discard - didDiscard = true; - } else { - buff[writeLength++] = c; - } - } - if (writeLength > 0) { - wrapped.write(buff, 0, writeLength); - } - } - - public boolean didDiscard() { - return didDiscard; - } -} diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/DOMHelper.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/DOMHelper.java deleted file mode 100644 index b62d404..0000000 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/DOMHelper.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Created on Dec 20, 2004 - */ -package org.apereo.openequella.integration.blackboard.common.propbag; - -import java.util.ArrayList; -import java.util.List; - -import org.w3c.dom.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.Node; - -/** - * @author Nicholas Read - */ -final class DOMHelper { - private DOMHelper() { - throw new IllegalAccessError("Do not invoke"); //$NON-NLS-1$ - } - - /** - * Removes namespace from a given path element. - */ - static String stripNamespace(String name) { - int index = name.indexOf(':'); - if (index >= 0) { - name = name.substring(index + 1); - } - return name; - } - - /** - * Removes namespace from a given path element. - */ - static String stripAttribute(String name) { - if (name.startsWith("@")) //$NON-NLS-1$ - { - return name.substring(1); - } else { - return name; - } - } - - /** - * Retrieves the text value from a node's child text node. - */ - static String getValueForNode(Node node, String defaultValue) { - String value = null; - if (node != null) { - switch (node.getNodeType()) { - case Node.ELEMENT_NODE: { - Node textNode = node.getFirstChild(); - if (textNode != null) { - value = textNode.getNodeValue(); - } - break; - } - - case Node.ATTRIBUTE_NODE: { - value = node.getNodeValue(); - break; - } - - default: - break; - } - } - - if (value == null) { - return defaultValue; - } else { - return value; - } - } - - /** - * Retrieves the text value from a node's child text node. - */ - static void setValueForNode(Node node, String value) { - if (node != null) { - switch (node.getNodeType()) { - case Node.ELEMENT_NODE: { - Node child = node.getFirstChild(); - if (child == null) { - Document doc = node.getOwnerDocument(); - node.appendChild(doc.createTextNode(value)); - } else { - child.setNodeValue(value); - } - break; - } - - case Node.ATTRIBUTE_NODE: { - - Attr attribute = (Attr) node; - attribute.getOwnerElement().setAttribute(attribute.getName(), value); - break; - } - - default: - break; - } - } - } - - static Node findNext(Node child, String nodeName) { - for (; child != null; child = child.getNextSibling()) { - if (child.getNodeType() == Node.ELEMENT_NODE) { - if (nodeName == null || nodeName.equals("*") //$NON-NLS-1$ - || nodeName.equals(DOMHelper.stripNamespace(child.getNodeName()))) { - return child; - } - } - } - return null; - } - - /** - * @return a vector containing all the delimited String sections - */ - static List splitPath(String path) { - List parts = new ArrayList(); - - String[] split = path.split("/"); //$NON-NLS-1$ - for (int i = 0; i < split.length; i++) { - if (split[i].length() > 0) { - boolean isLastPart = i == split.length - 1; - if (!isLastPart && split[i].indexOf('@') >= 0) { - throw new IllegalArgumentException("Attribute must be last component of path"); //$NON-NLS-1$ - } - parts.add(split[i]); - } - } - - return parts; - } - - static boolean hasChildElement(Node node) { - for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) { - if (child.getNodeType() == Node.ELEMENT_NODE) { - return true; - } - } - return false; - } - - static boolean removeNode(Node node) { - if (node != null) { - switch (node.getNodeType()) { - case Node.ELEMENT_NODE: { - Node parent = node.getParentNode(); - if (parent != null) { - parent.removeChild(node); - return true; - } - break; - } - - case Node.ATTRIBUTE_NODE: { - Attr attr = (Attr) node; - attr.getOwnerElement().removeAttribute(attr.getName()); - return true; - } - } - } - return false; - } -} \ No newline at end of file diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/PropBagMin.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/PropBagMin.java deleted file mode 100644 index a4ab834..0000000 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/PropBagMin.java +++ /dev/null @@ -1,845 +0,0 @@ -package org.apereo.openequella.integration.blackboard.common.propbag; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.Reader; -import java.io.StringReader; -import java.io.StringWriter; -import java.io.Writer; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; - -import org.w3c.dom.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.DocumentType; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; - -import com.google.common.base.Throwables; -import com.google.common.collect.Lists; -import com.google.common.io.Closeables; - -/** - * A subset of the actual PropBagEx class. Blackboard makes me cry, they still - * haven't fixed the upload webservice problem. - */ -@SuppressWarnings("nls") -public class PropBagMin { - private static final String WILD = "*"; - private static final String PATH_SEP = "/"; - private static final String BLANK = ""; - private static final String ATTR = "@"; - - private static DocumentBuilderFactory factory; - private Element m_elRoot; - - static { - if (factory == null) { - factory = DocumentBuilderFactory.newInstance(); - factory.setValidating(false); - - try { - factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); - } catch (ParserConfigurationException e) { - // nothing - } catch (NoSuchMethodError nup) { - // java 1.4,doesn't like it - } - factory.setNamespaceAware(false); - } - } - - /** - * Creates a new PropBagEx rooted at the given node. - * - * @param node - * The node to root the new PropBagEx at. - * @param sameDocument - * Use the nodes document for the new PropBagEx. - */ - private PropBagMin(final Node n, final boolean sameDocument) { - if (sameDocument) { - m_elRoot = (Element) n; - } else { - Document doc; - try { - DocumentBuilder builder; - synchronized (factory) { - builder = factory.newDocumentBuilder(); - } - doc = builder.getDOMImplementation().createDocument(null, null, null); - - final Node imp = importNode(doc, n, true); - doc.appendChild(imp); - - m_elRoot = (Element) imp; - } catch (final ParserConfigurationException pce) { - throw propagate(pce); - } - } - } - - /** - * Creates a new PropBagEx from the given string. - * - * @param szXML - * The string to read. - * @throws PropBagExException - * if the XML being read is invalid. - */ - public PropBagMin(final String szXML) { - setXML(new StringReader(szXML)); - } - - /** - * Creates a new PropBagEx from the given stream. - * - * @param is - * The stream to read from. - * @throws PropBagExException - * if the XML being read is invalid. - */ - public PropBagMin(final InputStream is) { - setXML(new UnicodeReader(is, "UTF-8")); - } - - /** - * A helper method that should be called by each method before using - * #getNodeHelper(..) or such. - */ - private void ensureRoot() { - if (m_elRoot == null) { - clear(); - } - } - - @SuppressWarnings("null") - private Node lookupNode(final Node parent, String nodeName, final int index, final boolean create) { - Node foundNode = null; - - int nNumFound = 0; - final Document doc = parent.getOwnerDocument(); - - final boolean isAttribute = nodeName.startsWith(ATTR); - nodeName = DOMHelper.stripAttribute(nodeName); - if (isAttribute) { - foundNode = ((Element) parent).getAttributeNode(nodeName); - } else { - nodeName = DOMHelper.stripNamespace(nodeName); - final boolean matchAny = nodeName.equals(WILD); - - final NodeList children = parent.getChildNodes(); - for (int i = 0; i < children.getLength() && foundNode == null; i++) { - final Node child = children.item(i); - if (child.getNodeType() == Node.ELEMENT_NODE) { - final String childName = DOMHelper.stripNamespace(child.getNodeName()); - if (matchAny || nodeName.equals(childName)) { - if (nNumFound != index) { - nNumFound++; - } else { - foundNode = child; - break; - } - } - } - } - } - - if (foundNode == null && create == true) { - // If the Index is 0 and we didn't find a node or if the number - // found (which is not zero based) equals the index (which is) - // then this is the same as saying index is one more that the - // number of nodes that exist then add a new child node. - if (index == 0 || nNumFound == index) { - if (isAttribute) { - ((Element) parent).setAttribute(nodeName, BLANK); - foundNode = ((Element) parent).getAttributeNode(nodeName); - } else { - foundNode = doc.createElement(nodeName); - parent.appendChild(foundNode); - } - } else { - throw new IndexOutOfBoundsException(); - } - } - return foundNode; - } - - private int getIndexValue(final String[] aszNodeName) { - aszNodeName[1] = aszNodeName[0]; - - final int index = aszNodeName[0].indexOf('['); - if (index >= 0) { - aszNodeName[1] = aszNodeName[0].substring(0, index); - - // Sanity Check - if (index + 1 < aszNodeName[0].length()) { - final String szIndex = aszNodeName[0].substring(index + 1, aszNodeName[0].length() - 1); - return Integer.parseInt(szIndex); - } - } - - // Index is the first instance. - return 0; - } - - /** - * Gets the node for the given path. - */ - private Node getNodeHelper(final String path, final boolean bCreate, final boolean bNew) { - // Split on the path identifier - final List pathComponents = DOMHelper.splitPath(path); - - Node node = m_elRoot; - final Document doc = node.getOwnerDocument(); - - final Iterator iter = pathComponents.iterator(); - while (iter.hasNext() && node != null) { - final String[] aszNodeName = new String[2]; - aszNodeName[0] = iter.next(); - - // Extract the index if that exists - final int nIndex = getIndexValue(aszNodeName); - if (bNew && !iter.hasNext()) { - final Node child = doc.createElement(aszNodeName[1]); - node.appendChild(child); - return child; - } else { - node = lookupNode(node, aszNodeName[1], nIndex, bCreate); - } - } - - return node; - } - - private Writer genXML(final Writer sbuf, final Node subRoot) { - try { - boolean bEndElem = false; - final int type = subRoot.getNodeType(); - switch (type) { - case Node.DOCUMENT_TYPE_NODE: { - final DocumentType doctype = (DocumentType) subRoot; - sbuf.write("\n"); - // doc.getDoctype(); - // System.out.println(""); - break; - } - case Node.ELEMENT_NODE: { - sbuf.write('<'); - sbuf.write(subRoot.getNodeName()); - final NamedNodeMap nnm = subRoot.getAttributes(); - if (nnm != null) { - final int len = nnm.getLength(); - Attr attr; - for (int i = 0; i < len; i++) { - attr = (Attr) nnm.item(i); - sbuf.write(' ' + attr.getNodeName() + "=\"" + ent(attr.getNodeValue()) + '"'); - } - } - // Check for an empty parent element - // no children, or a single TEXT_NODE with length() == 0 - final Node child = subRoot.getFirstChild(); - if (child == null || (child.getNodeType() == Node.TEXT_NODE - && (child.getNextSibling() == null && child.getNodeValue().length() == 0))) { - sbuf.write(PATH_SEP); - } else { - bEndElem = true; - } - sbuf.write('>'); - break; - } - case Node.ENTITY_REFERENCE_NODE: { - - sbuf.write('&' + subRoot.getNodeName() + ';'); - break; - } - case Node.CDATA_SECTION_NODE: { - sbuf.write(""); - break; - } - case Node.TEXT_NODE: { - sbuf.write(ent(subRoot.getNodeValue())); - break; - } - case Node.PROCESSING_INSTRUCTION_NODE: { - sbuf.write(" 0) { - sbuf.write(' '); - sbuf.write(data); - } - sbuf.write("?>"); - break; - } - case Node.COMMENT_NODE: { - sbuf.write(""); - break; - } - } - - for (Node child = subRoot.getFirstChild(); child != null; child = child.getNextSibling()) { - genXML(sbuf, child); - } - - if (bEndElem) { - sbuf.write(""); - } - } catch (final IOException e) { - throw propagate(e); - } - - return sbuf; - } - - private String ent(final String text) { - final StringBuilder szOut = new StringBuilder(); - final char[] chars = text.toCharArray(); - for (final char ch : chars) { - switch (ch) { - case '<': - szOut.append("<"); - break; - - case '>': - szOut.append(">"); - break; - - case '&': - szOut.append("&"); - break; - - case '"': - szOut.append("""); - break; - - default: - // http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char - // regular displayable ASCII: - if (ch == 0xA || ch == 0xD || ch == 0x9 || (ch >= 0x20 && ch <= 0x007F)) { - szOut.append(ch); - } else if ((ch > 0x007F && ch <= 0xD7FF) || (ch >= 0xE000 && ch <= 0xFFFD) - || (ch >= 0x10000 && ch <= 0x10FFFF)) { - szOut.append("&#x"); - szOut.append(hex(ch)); - szOut.append(';'); - } - // else we discard the character entirely. - // It CANNOT be placed in XML - break; - } - } - return szOut.toString(); - } - - private String hex(final char c) { - final String hexed = Integer.toHexString(c); - final int deficit = 4 - hexed.length(); - // wooo, unrolled loops - switch (deficit) { - case 1: - return "0" + hexed; - case 2: - return "00" + hexed; - case 3: - return "000" + hexed; - default: - return hexed; - } - } - - private Node getRootDoc() { - if (m_elRoot.getOwnerDocument().getDocumentElement() == m_elRoot) { - return m_elRoot.getOwnerDocument(); - } else { - return m_elRoot; - } - } - - @Override - public String toString() { - ensureRoot(); - // genXML returns the same StringBuffer that we pass in. - final StringWriter stringWriter = new StringWriter(); - genXML(new BadCharacterFilterWriter(stringWriter), getRootDoc()); - return stringWriter.toString(); - } - - /** - * Iterates over all nodes matching the given parent and name. - */ - private abstract static class InternalIterator implements Iterator, Iterable { - protected Node parent; - protected String name; - protected Node upto; - protected Node last; - protected Node root; - - public InternalIterator(final Node parent, final Node first, final String name, final Node root) { - this.parent = parent; - this.name = name; - this.root = root; - upto = first; - if (parent == null) { - upto = null; - } else if (upto == null) { - upto = DOMHelper.findNext(parent.getFirstChild(), name); - } - } - - protected void moveOn() { - last = upto; - if (upto == root) { - upto = null; - } else { - upto = DOMHelper.findNext(last.getNextSibling(), name); - } - } - - /* - * (non-Javadoc) - * - * @see java.lang.Iterable#iterator() - */ - @Override - public Iterator iterator() { - return this; - } - - /* - * (non-Javadoc) - * - * @see java.util.Iterator#remove() - */ - @Override - public void remove() { - if (last == null) { - throw new IllegalStateException(); - } - parent.removeChild(last); - } - - /* - * (non-Javadoc) - * - * @see java.util.Iterator#hasNext() - */ - @Override - public boolean hasNext() { - return upto != null; - } - - /* - * (non-Javadoc) - * - * @see java.util.Iterator#next() - */ - @Override - public T next() { - moveOn(); - return getNextValue(); - } - - protected abstract T getNextValue(); - } - - /** - * Iterates over all nodes matching the given parent and name. The iteration - * will return PropBagEx's rooted at each node. - */ - public static class PropBagIterator extends InternalIterator { - public PropBagIterator(final Node parent, final Node first, final String name, final Node root) { - super(parent, first, name, root); - } - - @Override - protected PropBagMin getNextValue() { - return new PropBagMin(last, true); - } - } - - /** - * Iterates over all nodes from the parent, that match the given path at any of - * it's components. - */ - private abstract static class ListOfNodesIterator implements Iterator, Iterable { - protected final LinkedList nodes = new LinkedList(); - - protected Node last; - - protected void moveOn() { - last = nodes.removeFirst(); - } - - /* - * (non-Javadoc) - * - * @see java.lang.Iterable#iterator() - */ - @Override - public Iterator iterator() { - return this; - } - - /* - * (non-Javadoc) - * - * @see java.util.Iterator#remove() - */ - @Override - public void remove() { - if (last == null) { - throw new IllegalStateException(); - } - last.getParentNode().removeChild(last); - } - - /* - * (non-Javadoc) - * - * @see java.util.Iterator#hasNext() - */ - @Override - public boolean hasNext() { - return !nodes.isEmpty(); - } - - /* - * (non-Javadoc) - * - * @see java.util.Iterator#next() - */ - @Override - public T next() { - moveOn(); - return getNextValue(); - } - - protected abstract T getNextValue(); - } - - /** - * Iterates over all nodes from the parent, that match the given path at any of - * it's components. - */ - private abstract static class InternalThoroughIterator extends ListOfNodesIterator { - public InternalThoroughIterator(final Node parent, final String path) { - final List names = Lists.newArrayList(path.split(PATH_SEP)); - for (final Iterator iter = names.iterator(); iter.hasNext();) { - final String p = iter.next(); - if (p.trim().length() == 0) { - iter.remove(); - } - } - - if (!names.isEmpty()) { - findAllNodes(parent, names, 0); - } - } - - protected void findAllNodes(final Node parent, final List names, final int index) { - final String path = names.get(index); - if (!path.startsWith(ATTR)) { - Node find = DOMHelper.findNext(parent.getFirstChild(), path); - while (find != null) { - if (index == names.size() - 1) { - nodes.add(find); - } else { - findAllNodes(find, names, index + 1); - } - find = DOMHelper.findNext(find.getNextSibling(), path); - } - } else { - if (index == names.size() - 1) { - final Node attr = parent.getAttributes().getNamedItem(path.substring(1)); - nodes.add(attr); - } else { - throw new RuntimeException("Xpath has an attribute component before the last component"); - } - } - } - } - - /** - * Iterates over all nodes from the parent, that match the given path at any of - * it's components. The iteration will return PropBag's rooted at each of each - * nodes. - */ - public static class PropBagMinThoroughIterator extends InternalThoroughIterator { - public PropBagMinThoroughIterator(final Node parent, final String name) { - super(parent, name); - } - - @Override - protected PropBagMin getNextValue() { - return new PropBagMin(last, true); - } - } - - /** - * Iterates over all nodes from the parent, that match the given path at any of - * it's components. The iteration will return each Node. - */ - public static class NodeThoroughIterator extends InternalThoroughIterator { - public NodeThoroughIterator(final Node parent, final String name) { - super(parent, name); - } - - @Override - protected Node getNextValue() { - return last; - } - } - - /** - * Creates an iterator which return's PropBagEx's for all children of the root - * element - */ - public PropBagIterator iterator() { - ensureRoot(); - return new PropBagIterator(m_elRoot, null, null, m_elRoot); - } - - /** - * Creates an iterator which return's PropBagEx's for each child for the path - * given
- * E.g.
- * /path/node/item
- * Will iterate over all the "item" nodes. - * - * @param path - * @return - */ - public PropBagIterator iterator(final String path) { - ensureRoot(); - - String name = null; - Node parent = null; - - final Node node = getNodeHelper(path, false, false); - if (node != null) { - parent = node.getParentNode(); - - // see Jira Defect TLE-1293 : - // http://apps.dytech.com.au/jira/browse/TLE-1293 - name = DOMHelper.stripNamespace(node.getNodeName()); - if (path.endsWith(WILD)) { - name = WILD; - } - - } - return new PropBagIterator(parent, node, name, m_elRoot); - } - - /** - * Iterates over all nodes that match the given path, like an XSLT does. - */ - public PropBagMinThoroughIterator iterateAll(final String path) { - ensureRoot(); - return new PropBagMinThoroughIterator(m_elRoot, path); - } - - private void setXML(final Reader reader) { - BufferedReader filterer = null; - try { - filterer = new BufferedReader(new BadCharacterFilterReader(reader)); - DocumentBuilder builder; - - synchronized (factory) { - builder = factory.newDocumentBuilder(); - } - Document doc = builder.parse(new InputSource(filterer)); - - // Get the root element - m_elRoot = doc.getDocumentElement(); - } catch (Exception ex) { - throw propagate(ex); - } finally { - if (filterer != null) { - try { - Closeables.close(filterer, false); - } catch (IOException e) { - // Whatever - } - } - } - } - - /** - * retrieves the text delimited by the given node's tags - * - * @param path - * full name of the node, parents qualified by '/' - * @return String content of element - */ - public String getNode(final String path) { - return getNode(path, BLANK); - } - - /** - * retrieves the text delimited by the given node's tags - * - * @param path - * full name of the node, parents qualified by '/' - * @return String content of element - */ - public String getNode(final String path, final String defaultValue) { - ensureRoot(); - final Node oNode = getNodeHelper(path, false, false); - return DOMHelper.getValueForNode(oNode, defaultValue); - } - - /** - * Retrieves a node as an int value given it's name. If the node does not exist - * or its value is an invalid integer, then the default value is returned. - * - * @param szFullNodeName - * full name of the node, parents qualified by '/' - * @param defaultValue - * A default value to return if the node does not exist. - * @return value of the node. - */ - public int getIntNode(final String path, final int defaultValue) { - ensureRoot(); - try { - return Integer.parseInt(getNode(path)); - } catch (final NumberFormatException ex) { - return defaultValue; - } - } - - public void setNode(final String path, final String value) { - ensureRoot(); - final Node node = getNodeHelper(path, true, false); - DOMHelper.setValueForNode(node, value); - } - - /** - * Checks to see if a node exists - * - * @param path - * The path to the node. - * @return true if one or more nodes for this path exist - */ - public boolean nodeExists(final String path) { - ensureRoot(); - Node node = getNodeHelper(path, false, false); - return node != null; - } - - /** - * Creates a new PropBag rooted at the given path, yet sharing the same DOM - * nodes as the creator. - * - * @param path - * Xpath to the root of the new tree. - * @return PropBagEx PropBag rooted at the subtree, or null if the path does not - * exist. - */ - public PropBagMin getSubtree(final String path) { - ensureRoot(); - final Element root = (Element) getNodeHelper(path, false, false); - - if (root == null) { - return null; - } else { - return new PropBagMin(root, true); - } - } - - /** - * Returns the name of the root element. - * - * @return the name of the node. - */ - public String getNodeName() { - ensureRoot(); - return getNodeHelper(BLANK, false, false).getNodeName(); - } - - private Node importNode(final Document doc, final Node n, final boolean bDeep) { - Node dest; - final int type = n.getNodeType(); - switch (type) { - case Node.ELEMENT_NODE: - dest = doc.createElement(n.getNodeName()); - final NamedNodeMap nnm = n.getAttributes(); - final int nnmCount = nnm.getLength(); - for (int i = 0; i < nnmCount; i++) { - final Attr attr = (Attr) nnm.item(i); - ((Element) dest).setAttribute(attr.getName(), attr.getValue()); - } - break; - - case Node.TEXT_NODE: - dest = doc.createTextNode(n.getNodeValue()); - break; - - case Node.CDATA_SECTION_NODE: - dest = doc.createCDATASection(n.getNodeValue()); - break; - - case Node.ENTITY_REFERENCE_NODE: - dest = doc.createEntityReference(n.getNodeValue()); - break; - - // see Jira Defect TLE-1832 : - // http://apps.dytech.com.au/jira/browse/TLE-1832 - case Node.COMMENT_NODE: - dest = doc.createComment(n.getNodeValue()); - break; - - default: - throw new RuntimeException("Unsupported DOM Node: " + type); - } - if (bDeep) { - for (Node child = n.getFirstChild(); child != null; child = child.getNextSibling()) { - dest.appendChild(importNode(doc, child, true)); - } - } - return dest; - } - - private void clear() { - try { - Document doc; - synchronized (factory) { - doc = factory.newDocumentBuilder().newDocument(); - } - - // Create the empty Propbag - final Element root = doc.createElement("xml"); - doc.appendChild(root); - - // Get the root element - m_elRoot = doc.getDocumentElement(); - } catch (final ParserConfigurationException pce) { - throw propagate(pce); - } - } - - private RuntimeException propagate(Throwable t) { - return Throwables.propagate(t); - } -} \ No newline at end of file diff --git a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/UnicodeReader.java b/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/UnicodeReader.java deleted file mode 100644 index dc9d06c..0000000 --- a/oeqPrimaryWS/src/main/java/org/apereo/openequella/integration/blackboard/common/propbag/UnicodeReader.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Original pseudocode : Thomas Weidenfeller Implementation tweaked: Aki - * Nieminen http://www.unicode.org/unicode/faq/utf_bom.html BOMs: 00 00 FE FF = - * UTF-32, big-endian FF FE 00 00 = UTF-32, little-endian FE FF = UTF-16, - * big-endian FF FE = UTF-16, little-endian EF BB BF = UTF-8 - */ -package org.apereo.openequella.integration.blackboard.common.propbag; - -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.PushbackInputStream; -import java.io.Reader; - -/** - * Generic unicode textreader, which will use BOM mark to identify the encoding - * to be used. - */ -public class UnicodeReader extends Reader { - private static final int BOM_SIZE = 4; - - private PushbackInputStream internalIn; - private InputStreamReader internalIn2 = null; - private String defaultEnc; - - public UnicodeReader(InputStream in, String defaultEnc) { - internalIn = new PushbackInputStream(in, BOM_SIZE); - this.defaultEnc = defaultEnc; - } - - public String getDefaultEncoding() { - return defaultEnc; - } - - public String getEncoding() { - if (internalIn2 == null) { - return null; - } - return internalIn2.getEncoding(); - } - - /** - * Read-ahead four bytes and check for BOM marks. Extra bytes are unread back to - * the stream, only BOM bytes are skipped. - */ - @SuppressWarnings("nls") - protected void init() throws IOException { - if (internalIn2 != null) { - return; - } - - String encoding; - int unread; - byte bom[] = new byte[BOM_SIZE]; - int n = internalIn.read(bom, 0, bom.length); - - if ((bom[0] == (byte) 0xEF) && (bom[1] == (byte) 0xBB) && (bom[2] == (byte) 0xBF)) { - encoding = "UTF-8"; - unread = n - 3; - } else if ((bom[0] == (byte) 0xFE) && (bom[1] == (byte) 0xFF)) { - encoding = "UTF-16BE"; - unread = n - 2; - } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE)) { - encoding = "UTF-16LE"; - unread = n - 2; - } else if ((bom[0] == (byte) 0x00) && (bom[1] == (byte) 0x00) && (bom[2] == (byte) 0xFE) - && (bom[3] == (byte) 0xFF)) { - encoding = "UTF-32BE"; - unread = n - 4; - } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE) && (bom[2] == (byte) 0x00) - && (bom[3] == (byte) 0x00)) { - encoding = "UTF-32LE"; - unread = n - 4; - } else { - // Unicode BOM mark not found, unread all bytes - encoding = defaultEnc; - unread = n; - } - - if (unread > 0) { - internalIn.unread(bom, (n - unread), unread); - } else if (unread < -1) { - internalIn.unread(bom, 0, 0); - // Use given encoding - } - if (encoding == null) { - internalIn2 = new InputStreamReader(internalIn); - } else { - internalIn2 = new InputStreamReader(internalIn, encoding); - } - } - - @Override - public void close() throws IOException { - init(); - internalIn2.close(); - } - - @Override - public int read(char[] cbuf, int off, int len) throws IOException { - init(); - return internalIn2.read(cbuf, off, len); - } -} \ No newline at end of file From 8060e5dd6c08d2cc7f91a691ac85bb423b2e1f44 Mon Sep 17 00:00:00 2001 From: C Beach Date: Thu, 21 Feb 2019 11:29:37 -0600 Subject: [PATCH 10/14] #4 Fixed the LTI launcher --- .../lti/FixedBasicLtiLauncher.java | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/lti/FixedBasicLtiLauncher.java b/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/lti/FixedBasicLtiLauncher.java index 0b65f20..8885f51 100644 --- a/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/lti/FixedBasicLtiLauncher.java +++ b/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/lti/FixedBasicLtiLauncher.java @@ -38,8 +38,6 @@ @SuppressWarnings("nls") // @NonNullByDefault public class FixedBasicLtiLauncher extends BasicLTILauncher { - // Sigh, BasicLTILauncher._parameters is private in SP11 - private final Map params2; private String ourUrl; private FixedBasicLtiLauncher(String fullUrl, String clientId, String clientSecret, /* @@ -50,15 +48,6 @@ private FixedBasicLtiLauncher(String fullUrl, String clientId, String clientSecr ourUrl = fullUrl; - // Ugh, _parameters is protected in SP13 but *private* in SP11 - try { - final Field paramsField = BasicLTILauncher.class.getDeclaredField("_parameters"); - paramsField.setAccessible(true); - params2 = (Map) paramsField.get(this); - } catch (Exception e) { - throw new RuntimeException(e); - } - // Put the query string values into the parameters (does this double // post?) final int question = fullUrl.indexOf('?'); @@ -69,9 +58,9 @@ private FixedBasicLtiLauncher(String fullUrl, String clientId, String clientSecr for (String param : params) { final String[] nameVal = param.split("="); if (nameVal.length == 2) { - params2.put(decodePercent(nameVal[0]), decodePercent(nameVal[1])); + _parameters.put(decodePercent(nameVal[0]), decodePercent(nameVal[1])); } else { - params2.put(decodePercent(nameVal[0]), null); + _parameters.put(decodePercent(nameVal[0]), null); } } } @@ -110,7 +99,7 @@ public FixedBasicLtiLauncher addGradingInformation(HttpServletRequest request, W final Id courseId = BbUtil.getCourseId(request.getParameter(COURSE_ID)); try { final CourseMembership courseMembership = user.getCourseMembership(courseId); - return (FixedBasicLtiLauncher) super.addGradingInformation(content.getContent(), courseMembership); + return (FixedBasicLtiLauncher) super.addGradingInformation(request, content.getContent(), courseMembership); } catch (KeyNotFoundException knf) { // No enrollment, don't add anything return this; @@ -143,8 +132,8 @@ public BasicLTILauncher addUserInformation(User user, CourseMembership membershi } public FixedBasicLtiLauncher addPostData(String key, /* @Nullable */String value) { - params2.put(key, value); - return this; + _parameters.put(key, value); + return this; } public FixedBasicLtiLauncher addReturnUrl(String returnUrl) { From a515944447bdbfbc20adc9650b4500277546647b Mon Sep 17 00:00:00 2001 From: Aaron Holland Date: Fri, 22 Feb 2019 17:53:06 +1100 Subject: [PATCH 11/14] 4 remove all tag libs. update web.xml to latest --- .../blackboard/buildingblock/ContextTag.java | 37 - .../WEB-INF/bb-context-config.properties | 1 + oeqPrimaryB2/src/main/webapp/WEB-INF/web.xml | 35 +- oeqPrimaryB2/src/main/webapp/admin/config.jsp | 3 +- .../src/main/webapp/contribute/create.jsp | 38 +- .../src/main/webapp/contribute/modify.jsp | 139 +- .../main/webapp/contribute/modify_proc.jsp | 34 +- oeqPrimaryB2/src/main/webapp/error.jsp | 34 +- oeqPrimaryB2/src/main/webapp/portal/view.jsp | 1 - .../src/main/webapp/taglibs/config/bbNG.tld | 11674 ---------------- .../src/main/webapp/taglibs/config/bbNG.xsl | 93 - .../taglibs/config/struts/struts-bean.tld | 1153 -- .../taglibs/config/struts/struts-html.tld | 9283 ------------ .../taglibs/config/struts/struts-logic.tld | 1891 --- .../taglibs/config/struts/struts-nested.tld | 5051 ------- oeqPrimaryB2/src/main/webapp/taglibs/tle.tld | 15 - 16 files changed, 123 insertions(+), 29359 deletions(-) delete mode 100644 oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/ContextTag.java create mode 100644 oeqPrimaryB2/src/main/webapp/WEB-INF/bb-context-config.properties delete mode 100644 oeqPrimaryB2/src/main/webapp/taglibs/config/bbNG.tld delete mode 100644 oeqPrimaryB2/src/main/webapp/taglibs/config/bbNG.xsl delete mode 100644 oeqPrimaryB2/src/main/webapp/taglibs/config/struts/struts-bean.tld delete mode 100644 oeqPrimaryB2/src/main/webapp/taglibs/config/struts/struts-html.tld delete mode 100644 oeqPrimaryB2/src/main/webapp/taglibs/config/struts/struts-logic.tld delete mode 100644 oeqPrimaryB2/src/main/webapp/taglibs/config/struts/struts-nested.tld delete mode 100644 oeqPrimaryB2/src/main/webapp/taglibs/tle.tld diff --git a/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/ContextTag.java b/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/ContextTag.java deleted file mode 100644 index 6eca0b0..0000000 --- a/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/ContextTag.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.apereo.openequella.integration.blackboard.buildingblock; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.jsp.tagext.Tag; -import javax.servlet.jsp.tagext.TagSupport; -import javax.servlet.jsp.tagext.TryCatchFinally; - -//import com.tle.blackboard.buildingblock.data.WrappedUser; - -// @NonNullByDefault -public class ContextTag extends TagSupport implements TryCatchFinally { - // private WrappedUser user; - - @Override - public int doStartTag() { - try { - HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); - // user = WrappedUser.getUser(request); - } catch (Exception t) { - // TODO: Can this safely be ignored? - } - return Tag.EVAL_BODY_INCLUDE; - } - - @Override - public void doCatch(Throwable t) throws Throwable { - // Never happen - } - - @Override - public void doFinally() { - // if( user != null ) - // { - // user.clearContext(); - // } - } -} diff --git a/oeqPrimaryB2/src/main/webapp/WEB-INF/bb-context-config.properties b/oeqPrimaryB2/src/main/webapp/WEB-INF/bb-context-config.properties new file mode 100644 index 0000000..5bb5949 --- /dev/null +++ b/oeqPrimaryB2/src/main/webapp/WEB-INF/bb-context-config.properties @@ -0,0 +1 @@ +com.blackboard.tomcat.servletcontainer.jarscanner.tldJars=bb-taglibs.jar|struts-taglib-.*\\.jar \ No newline at end of file diff --git a/oeqPrimaryB2/src/main/webapp/WEB-INF/web.xml b/oeqPrimaryB2/src/main/webapp/WEB-INF/web.xml index dc5b982..fc69699 100644 --- a/oeqPrimaryB2/src/main/webapp/WEB-INF/web.xml +++ b/oeqPrimaryB2/src/main/webapp/WEB-INF/web.xml @@ -1,6 +1,8 @@ - - + openEQUELLA openEQUELLA Integration with Blackboard @@ -66,32 +68,5 @@ text/plain - - - - /tle - /taglibs/tle.tld - - - - /bbNG - /taglibs/config/bbNG.tld - - - - http://struts.apache.org/tags-bean - /taglibs/config/struts/struts-bean.tld - - - http://struts.apache.org/tags-html - /taglibs/config/struts/struts-html.tld - - - http://struts.apache.org/tags-logic - /taglibs/config/struts/struts-logic.tld - - - http://struts.apache.org/tags-nested - /taglibs/config/struts/struts-nested.tld - + \ No newline at end of file diff --git a/oeqPrimaryB2/src/main/webapp/admin/config.jsp b/oeqPrimaryB2/src/main/webapp/admin/config.jsp index a4c299e..e91ee76 100644 --- a/oeqPrimaryB2/src/main/webapp/admin/config.jsp +++ b/oeqPrimaryB2/src/main/webapp/admin/config.jsp @@ -16,7 +16,6 @@ <%@page import="org.apereo.openequella.integration.blackboard.common.BbUtil"%> <%@page import="org.apereo.openequella.integration.blackboard.common.BbLogger"%> <%@ taglib uri="/bbNG" prefix="bbng"%> -<%@ taglib uri="/tle" prefix="tle"%> <%! WrappedUser user; @@ -129,7 +128,7 @@ int number = 1; <% if(message.length() > 0) { %> -
<%=message%>
+
<%=message%>
<% } %> diff --git a/oeqPrimaryB2/src/main/webapp/contribute/create.jsp b/oeqPrimaryB2/src/main/webapp/contribute/create.jsp index 2a9ebc6..0b97504 100644 --- a/oeqPrimaryB2/src/main/webapp/contribute/create.jsp +++ b/oeqPrimaryB2/src/main/webapp/contribute/create.jsp @@ -2,24 +2,22 @@ <%@page contentType="text/html;charset=UTF-8" errorPage="/error.jsp" %> <%@page import="org.apereo.openequella.integration.blackboard.buildingblock.data.WrappedContent"%> -<%@ taglib uri="/tle" prefix="tle"%> <%@ taglib uri="/bbNG" prefix="bbng"%> - - - <% - String error=""; - try - { - WrappedContent content = new WrappedContent(request); - content.startSelectionSession(request, response); - } - catch (Throwable t) - { - java.io.StringWriter errors = new java.io.StringWriter(); - t.printStackTrace(new java.io.PrintWriter(errors)); - error = errors.toString(); - } - %> - <%=error%> - - \ No newline at end of file + + + <% + String error=""; + try + { + WrappedContent content = new WrappedContent(request); + content.startSelectionSession(request, response); + } + catch (Throwable t) + { + java.io.StringWriter errors = new java.io.StringWriter(); + t.printStackTrace(new java.io.PrintWriter(errors)); + error = errors.toString(); + } + %> + <%=error%> + \ No newline at end of file diff --git a/oeqPrimaryB2/src/main/webapp/contribute/modify.jsp b/oeqPrimaryB2/src/main/webapp/contribute/modify.jsp index 7ea800e..5e378b5 100644 --- a/oeqPrimaryB2/src/main/webapp/contribute/modify.jsp +++ b/oeqPrimaryB2/src/main/webapp/contribute/modify.jsp @@ -4,77 +4,74 @@ <%@page import="org.apereo.openequella.integration.blackboard.common.BbUtil" %> <%@ taglib uri="/bbNG" prefix="bbng"%> -<%@ taglib uri="/tle" prefix="tle"%> - - <% - WrappedContent content = new WrappedContent(request); - %> - - - - - - - - -
- - - - - - <% String error = (String)request.getAttribute("error"); - if (error != null) - {%> - - <%=error%> - - <% } %> - - - - - - - - - - - <%=content.getHtml(request, true)%> - - - - - - Yes - No - - - - Yes - No - - - - Yes - No - +<% + WrappedContent content = new WrappedContent(request); +%> + + + + + + + + + + + + + + + <% String error = (String)request.getAttribute("error"); + if (error != null) + {%> + + <%=error%> + + <% } %> + + + + + + + + - - Yes - No - - - - - - + + <%=content.getHtml(request, true)%> + + + + + + Yes + No + - - -
-
-
\ No newline at end of file + + Yes + No + + + + Yes + No + + + + Yes + No + + + + + + + + + + + \ No newline at end of file diff --git a/oeqPrimaryB2/src/main/webapp/contribute/modify_proc.jsp b/oeqPrimaryB2/src/main/webapp/contribute/modify_proc.jsp index ea00ab5..dfb94cf 100644 --- a/oeqPrimaryB2/src/main/webapp/contribute/modify_proc.jsp +++ b/oeqPrimaryB2/src/main/webapp/contribute/modify_proc.jsp @@ -3,27 +3,23 @@ <%@page import="org.apereo.openequella.integration.blackboard.buildingblock.data.WrappedContent" %> <%@ taglib uri="/bbNG" prefix="bbng"%> -<%@ taglib uri="/tle" prefix="tle"%> <%-- Handles the 'save' from edit a resource content object (fancy way of saying oEQ link in Bb) --%> - - <% - WrappedContent content = new WrappedContent(request); - content.modify(request); - content.persist(request); - %> +<% + WrappedContent content = new WrappedContent(request); + content.modify(request); + content.persist(request); +%> - - + + - -

<%=content.getTitle()%>

- <%=content.getHtml(request, true)%> -
-
-
-
- -
\ No newline at end of file + +

<%=content.getTitle()%>

+ <%=content.getHtml(request, true)%> +
+
+
+ \ No newline at end of file diff --git a/oeqPrimaryB2/src/main/webapp/error.jsp b/oeqPrimaryB2/src/main/webapp/error.jsp index 354eee6..33023f0 100644 --- a/oeqPrimaryB2/src/main/webapp/error.jsp +++ b/oeqPrimaryB2/src/main/webapp/error.jsp @@ -4,10 +4,8 @@ <%@page isErrorPage="true"%> <%@taglib uri="/bbNG" prefix="bbng"%> -<%@taglib uri="/tle" prefix="tle"%> - -< + - - + + <% + String strException = exception.getMessage(); + if( strException != null && !strException.equals("") ) + { + strException = "An error has occurred"; + } + %> + <%=strException%> + + \ No newline at end of file diff --git a/oeqPrimaryB2/src/main/webapp/portal/view.jsp b/oeqPrimaryB2/src/main/webapp/portal/view.jsp index 6a2ed1f..98bed74 100644 --- a/oeqPrimaryB2/src/main/webapp/portal/view.jsp +++ b/oeqPrimaryB2/src/main/webapp/portal/view.jsp @@ -10,7 +10,6 @@ <%@taglib uri="http://struts.apache.org/tags-nested" prefix="n"%> <%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%> <%@taglib uri="http://struts.apache.org/tags-logic" prefix="l"%> -<%@ taglib uri="/tle" prefix="tle"%> <%@ taglib uri="/bbNG" prefix="bbng"%> <%! diff --git a/oeqPrimaryB2/src/main/webapp/taglibs/config/bbNG.tld b/oeqPrimaryB2/src/main/webapp/taglibs/config/bbNG.tld deleted file mode 100644 index 1b52366..0000000 --- a/oeqPrimaryB2/src/main/webapp/taglibs/config/bbNG.tld +++ /dev/null @@ -1,11674 +0,0 @@ - - - - - - 1.0 - bbNG - /bbNG - - Next-Gen UI Taglib. - - The Blackboard User Interface tag library encapsulates Blackboard user interface - elements to allow developers easy access to the Blackboard look and feel. - - - - - - jspBlock - blackboard.servlet.tags.ngui.JspBlockTag - JSP - - - - - - - jsBlock - blackboard.servlet.tags.ngui.JavascriptBlockTag - JSP - - - Example usage:
- <bbNG:jsBlock>
-    <script type="text/javascript"> .... </script>
- </bbNG:jsBlock> -

- ]]> -
-
- - - jsBundleMessage - blackboard.servlet.tags.ngui.JavascriptBundleMessageTag - empty - - - Example usage:
- <bbNG:jsBundleMessage bundle="common" key="common.ok" />
- <bbNG:jsBundleMessage bundle="some_other_bundle" key="common.ok" uniqueKey="otherOKString" />
-

- - JavaScript code can then use the message by calling: -

- - <script> -
- ... -
- page.bundle.getString( "common.ok", arg1, arg2, ...., argN ); -
- page.bundle.getString( "otherOKString", arg1, arg2, ...., argN ); -
- ... -
- </script> -
-

- ]]> -
- - bundle - true - true - Name of the resource bundle to load the message from. - - - key - true - true - Key in the message bundle for the message that should be loaded. - - - uniqueKey - false - true - - Unique key to use as reference the message string in the bundle in case - of bundle key name collisions within a page. The required (bundle) key - will be used if an unique key is not provided. If a unique key is provided, - then the bundle string can be only referenced by the unique key and not the key. - - -
- - - cssBlock - blackboard.servlet.tags.ngui.CssBlockTag - JSP - - - Example usage:
- <bbNG:cssBlock>
-    <style type="text/css"> .... </style>
- </bbNG:cssBlock> -

- ]]> -
-
- - - jsFile - blackboard.servlet.tags.ngui.JavascriptFileTag - empty - - - - - href - true - true - Path to the js file. - - - - - cssFile - blackboard.servlet.tags.ngui.CssFileTag - empty - - - - - href - true - true - Path to the css file. - - - - - copyright - blackboard.servlet.tags.ngui.CopyrightTag - empty - Standard HTML snippet for Blackboard copyright notice - - - - - - - genericPage - blackboard.servlet.tags.ngui.GenericPageTag - blackboard.servlet.tags.ngui.BasePageTei - JSP - - - - - navItem - false - true - Navigation Item handle for the nav item of the page. - - - onLoad - false - true - Body onLoad JavaScript handler. - - - title - false - true - Page title. - - - license - false - true - License key to check for access to this page. - - - entitlements - false - true - Array of Entitlements that are allowed to view this page. - - - entitlement - false - true - Entitlement required to view this page. - - - authentication - false - true - Whether this page requires the user to be logged in (Y/N). - - - contextOverride - false - true - The context override to use for this page (e.g. ADMIN_PANEL). - - - ctxId - false - true - - PageContext attribute name to stick the Context object in. If this is - not specified, then the attribute used will be "bbContext". - - - - wrapper - false - true - - Whether or not the wrapping page components (breadcrumb bar/title bar/ - content div) should be shown. Default true. - - - - bodyClass - false - true - - HTML class attribute for body tag. Default is none. - - - - outputBodyTags - false - true - - - - - - - - includedPage - blackboard.servlet.tags.ngui.IncludedPageTag - blackboard.servlet.tags.ngui.BasePageTei - JSP - - - - - navItem - false - true - Navigation Item handle for the nav item of the page. - - - onLoad - false - true - Body onLoad JavaScript handler. - - - license - false - true - License key to check for access to this page. - - - entitlements - false - true - Array of Entitlements that are allowed to view this page. - - - entitlement - false - true - Entitlement required to view this page. - - - authentication - false - true - Whether this page requires the user to be logged in (Y/N). - - - contextOverride - false - true - The context override to use for this page (e.g. ADMIN_PANEL). - - - ctxId - false - true - - PageContext attribute name to stick the Context object in. If this is - not specified, then the attribute used will be "bbContext". - - - - - - portalPage - blackboard.servlet.tags.ngui.PortalPageTag - blackboard.servlet.tags.ngui.BasePageTei - JSP - - - - - navItem - false - true - Navigation Item handle for the nav item of the page. - - - onLoad - false - true - Body onLoad JavaScript handler. - - - title - false - true - Page title. - - - license - false - true - License key to check for access to this page. - - - entitlements - false - true - Array of Entitlements that are allowed to view this page. - - - entitlement - false - true - Entitlement required to view this page. - - - authentication - false - true - Whether this page requires the user to be logged in (Y/N). - - - contextOverride - false - true - The context override to use for this page (e.g. ADMIN_PANEL). - - - ctxId - false - true - - PageContext attribute name to stick the Context object in. If this is - not specified, then the attribute used will be "bbContext". - - - - flush - false - true - - Whether the page is able to be incrementally flushed (default: false). - - When this is set to "true", the behavior of the scaffolding changes, and some of the - automatic magic will not work the same way. - - additional javascript blocks, html snippets, css files, onload javascript is put at the bottom - of the html file rather than at the top - - Some container divs are not output, so the JSP has to include them - - Inline receipts aren't automatically added (have to use the inlineReceipt tag) - - Breadcrumbs / Titlebar aren't automatically added - - - - - - adminConsolePage - blackboard.servlet.tags.ngui.AdminConsolePageTag - blackboard.servlet.tags.ngui.BasePageTei - JSP - - - - - navItem - false - true - Navigation Item handle for the nav item of the page. - - - onLoad - false - true - Body onLoad JavaScript handler. - - - title - false - true - Page title. - - - license - false - true - License key to check for access to this page. - - - entitlements - false - true - Array of Entitlements that are allowed to view this page. - - - entitlement - false - true - Entitlement required to view this page. - - - authentication - false - true - Whether this page requires the user to be logged in (Y/N). - - - contextOverride - false - true - The context override to use for this page (e.g. ADMIN_PANEL). - - - ctxId - false - true - - PageContext attribute name to stick the Context object in. If this is - not specified, then the attribute used will be "bbContext". - - - - flush - false - true - - Whether the page is able to be incrementally flushed (default: false). - - When this is set to "true", the behavior of the scaffolding changes, and some of the - automatic magic will not work the same way. - - additional javascript blocks, html snippets, css files, onload javascript is put at the bottom - of the html file rather than at the top - - Some container divs are not ouput, so the JSP has to include them - - Inline receipts aren't automatically added (have to use the inlineReceipt tag) - - Breadcrumbs / Titlebar aren't automatically added - - - - - - modulePage - blackboard.servlet.tags.ngui.module.ModulePageTag - blackboard.servlet.tags.ngui.BasePageTei - JSP - - - - - type - true - true - Type of the page. Must either be "admin" or "personalize", corresponding - to a moduleAdminPage or a modulePersonalizationPage. - - - title - false - true - Title to display in the titlebar - - - iconUrl - false - true - Location of the icon to display in the titlebar of the page - - - receiptPage - false - true - Whether this page is a receipt page. Default false. - - - recallUrl - false - true - Link target for the OK button (only takes effect when "receiptPage" is true - - - navItem - false - true - Navigation Item handle for the nav item of the page. - - - onLoad - false - true - Body onLoad JavaScript handler. - - - license - false - true - License key to check for access to this page. - - - entitlements - false - true - Array of Entitlements that are allowed to view this page. - - - entitlement - false - true - Entitlement required to view this page. - - - authentication - false - true - Whether this page requires the user to be logged in (Y/N). - - - contextOverride - false - true - The context override to use for this page (e.g. ADMIN_PANEL). - - - ctxId - false - true - - PageContext attribute name to stick the Context object in. If this is - not specified, then the attribute used will be "bbContext". - - - - formName - false - true - - Pass the formName if the type is "personalize". - - - - - - learningSystemPage - blackboard.servlet.tags.ngui.LearningSystemPageTag - blackboard.servlet.tags.ngui.BasePageTei - JSP - - - - - navItem - false - true - Navigation Item handle for the nav item of the page. - - - onLoad - false - true - Body onLoad JavaScript handler. - - - title - false - true - Page title. - - - license - false - true - License key to check for access to this page. - - - entitlements - false - true - Array of Entitlements that are allowed to view this page. - - - entitlement - false - true - Entitlement required to view this page. - - - authentication - false - true - Whether this page requires the user to be logged in (Y/N). - - - contextOverride - false - true - The context override to use for this page (e.g. ADMIN_PANEL). - - - ctxId - false - true - - PageContext attribute name to stick the Context object in. If this is - not specified, then the attribute used will be "bbContext". - - - - hideCourseMenu - false - true - Indicates if you want to actually display the course menu (default:false). - This would only be used on pages where you sometimes want the menu and sometimes not, such as - taking an assessment - if it is an 'open-in-new-window' assessment then this is true but otherwise - this is false - - - readModeAction - false - true - URL to use for the read mode in the toggle bar (defaults to window.location if not specified). - If set to javascript:xxx then xxx is evaluated as javascript instead of setting the window.location directly. - - - editModeAction - false - true - URL to use for the edit mode in the toggle bar (defaults to window.location if not specified). - If set to javascript:xxx then xxx is evaluated as javascript instead of setting the window.location directly. - - - hideEditToggle - false - true - Indicates if you want to actually display the toggle control (default:false). - This is currently being used for the Send Email tool for an integrated course. - - - disableEditToggle - false - true - - Indicates whether to disable the edit toggle (default:false). - - - - bodyClass - false - true - - HTML class attribute for body tag. Default is none. - - - - checkGuest - false - true - - If set to Y it allows access to guest and course geust users irrespective of the entitlement to access the page. Default is N. - - - - - - contentSystemPage - blackboard.servlet.tags.ngui.ContentSystemPageTag - blackboard.servlet.tags.ngui.BasePageTei - JSP - - - - - navItem - false - true - Navigation Item handle for the nav item of the page. - - - onLoad - false - true - Body onLoad JavaScript handler. - - - title - false - true - Page title. - - - license - false - true - License key to check for access to this page. - - - entitlements - false - true - Array of Entitlements that are allowed to view this page. - - - entitlement - false - true - Entitlement required to view this page. - - - authentication - false - true - Whether this page requires the user to be logged in (Y/N). - - - contextOverride - false - true - The context override to use for this page (e.g. ADMIN_PANEL). - - - ctxId - false - true - - PageContext attribute name to stick the Context object in. If this is - not specified, then the attribute used will be "bbContext". - - - - - - fileManagerPage - blackboard.servlet.tags.ngui.FileManagerPageTag - blackboard.servlet.tags.ngui.BasePageTei - JSP - - Renders the palette menu to embed view of FileManager. - - - - - outcomesSystemPage - blackboard.servlet.tags.ngui.OutcomesSystemPageTag - blackboard.servlet.tags.ngui.BasePageTei - JSP - - - - - navItem - false - true - Navigation Item handle for the nav item of the page. - - - cssClass - false - true - Space separated list of css classes to apply to page tag - - - onLoad - false - true - Body onLoad JavaScript handler. - - - title - false - true - Page title. - - - license - false - true - License key to check for access to this page. - - - entitlements - false - true - Array of Entitlements that are allowed to view this page. - - - entitlement - false - true - Entitlement required to view this page. - - - authentication - false - true - Whether this page requires the user to be logged in (Y/N). - - - contextOverride - false - true - The context override to use for this page (e.g. ADMIN_PANEL). - - - ctxId - false - true - - PageContext attribute name to stick the Context object in. If this is - not specified, then the attribute used will be "bbContext". - - - - showBreadcrumbBar - false - true - - Boolean toggle to show/hide the breadcrumb on the page. The default is true. - In general, pages should always have the breadcrumb bar unless PM and UX - explicitly specified that rendering of the breadcrumb bar should be suppressed. - - - - - - - receipt - blackboard.servlet.tags.ngui.ReceiptTag - JSP - Creates a receipt page using titleBar and button components - - type - false - true - - Type of receipt. Valid values: (SUCCESS, FAIL, DISABLED). If type is not set to a - supported value or is null, then default to successful receipt - - - - iconUrl - false - true - - A String containing the iconUrl. Should look like: - "/images/ci/icons/receiptsuccess_u.gif" - - - - pluginId - false - true - - The Id for the plugin (known by the plugin developer and used as the directory - root of the plugin, e.g "/Blackboard/plugins/*pluginId*/images/pluginimg.gif") - - - - title - false - true - - Page title for the receipt page. If not set, tag attempts to retrieve from the - navItem+context - - - - recallUrl - false - true - - OK button, if null will attempt to locate recallUrl from nav item or use - javascript back. ONLY SHOWN IF submitUrl and cancelUrl are both null - - - - cancelUrl - false - true - - Optional attribute to allow the receipt to have two buttons (although either may - be used alone) LINK rather than FORM_SUBMIT action. NOTE: previous documentation - referenced another attribute called submitUrl. This seems to have been replaced by - recallUrl? - - - - buttonName - false - true - Name of the button. - - - buttonAlt - false - true - Alternate text to display in a no-images browser - - - - - error - blackboard.servlet.tags.ngui.ErrorTag - empty - Handles exception UI - - exception - true - true - The exception that was thrown - - - - - errors - blackboard.servlet.tags.ErrorsTag - empty - - This is a replacement for the Struts html:error tag, which does not include the - internationalization features - ]]> - Note that, due to a dependency on Struts, and the fact that Struts 1.0 cannot be - loaded from the systemlib classloader, this class is NOT built into the - bb-taglib-api.jar. Instead, if you need to use this tag, you must include bb-common.jar - in your webapp. - - - name - false - true - - The name of the page context attribute containing the ActionErrors object to be displayed - - - - property - false - true - - The name of the property for which error messages should be returned, or null to - return all errors - - - - - - - pageHeader - blackboard.servlet.tags.ngui.PageHeaderTag - JSP - - - - - navItem - false - true - Navigation Item handle for the header. - - - instructions - false - true - Page-level instructions. - - - helpKey - false - true - - Bundle key for page-level instructions and extended instructions, in the format - foo.help. Extended instructions key will be parsed from foo.help and does not need to be passed. - - - - helpResolverClass - false - true - Resolver class that will handle dynamic text substitution for extended help. - - - banner - false - true - - Page banner to be placed above title. This attribute overrides any system - default value, if any, like course banner. - - - - - - pageTitleBar - blackboard.servlet.tags.ngui.PageTitleBarTag - JSP - - - - - title - false - true - - navItem attribute is provided to - either the page or page header tags-- the page title will be realized - from the nav item's label. - If nav item is not provided, a title must be provided when using this tag - ( either via the title attribute or text within the body of this tag ). - - This attribute can be defined simultaneously with the body of this tag for - accessibility. If using the body of this tag, consider using this - attribute to define the title attribute of the head tag in this page. - Otherwise, the body of this tag will be rendered in the head of the page. - ]]> - - - - iconUrl - false - true - Url for the icon. If not specified, a default icon will be used. - - - iconAlt - false - true - Alt text for the icon specified with iconUrl. If not specified, will default to the empty string. - - - showTitleBar - false - true - - Renders empty title bar with anchor for access key only, if set to false. This - attribute can be used to hide page title bar while still suppressing auto - generation of a default one by enclosing tags such as pageHeader or page tags. - - - - showIcon - false - true - - Indicates whether an icon should be shown on the title bar. Default: True - - - - - pageTitleBarContextMenu - blackboard.servlet.tags.ngui.PageTitleBarContextMenuTag - scriptless - - - - - dynamic - false - true - Whether this context menu is populated at run time. - - - menuGeneratorURL - false - true - Url that the dynamic options will be loaded from. - - - contextParameters - false - true - Context parameters needed for resolution of href's. - - - navItem - false - true - - Handle of the root navigation item to grab the context menu items from. - All children nav items of the specified item will be used to populate the context menu. - - - - menuLinkTitle - false - true - Text for the title attribute of the context menu link. If not specified will default to: Click to see options - - - orderKey - false - true - - base/config/internal/contextMenuOrder.properties - file to load the context menu item order from. - ]]> - - - - - - breadcrumbBar - blackboard.servlet.tags.ngui.BreadcrumbBarTag - JSP - - - - - navItem - false - true - - The database field used to uniquely identify the root nav item for the breadcrumb - bar. The bar gets the appropriate nav item from the handler, then traverses the - navigation tree to iteratively get the parents of the item. - - - - environment - false - true - - Whether we are viewing this navigation in the course, control panel, portal, etc. - Acceptable values are: - ]]> - ]]> - PORTAL - ]]> - COURSE - ]]> - CTRL_PANEL - ]]> - SYS_ADMIN - - - - isContent - false - true - - Indicates whether this navigation bar should render content components. If set, - the tag will use context information to display the "content" portion of the - breadcrumb - - - - target - false - true - - The default target for breadcrumb items in this bar, defaults to none. - - - - breadcrumbs - false - true - java.util.List - You can also pass in a List of Breadcrumb objects. - - - suppressCourse - false - true - Suppresses the current course from the view. Default: false. - - - includeCourseMenuIcon - false - true - - To display the courseMenuIcon in the breadcrumb. Default: false. - Should to include the Javascript to activate onClick for id "courseMapButton" - - - - assumeHelpPresent - false - true - - Defaults to 'false' in which case the page loading logic will iterate over the page to figure out if there is toggleable help. - If you know that your page has toggleable help then set this to 'true' to avoid this page-load-time javascript overhead. - - - - inFlow - false - true - - Defaults to 'false' in which case the breadcrumb is rendered in its usual location for the current page type. - If true the breadcrumb bar will render in place in the page flow. Usually used when a breadcrumb like display - is needed for example xxx > yyy > zzz. - Current implementation limits to one inFlow breadcrumb (but it can coexist with the regular breadcrumb). - InFlow breadcrumbs will render the 1st breadcrumb as a regular breadcrumb. - - - - skipLastContentCrumb - false - true - - @Since 9.1 - Defaults to 'false' in which case the breadcrumb will include crumbs up to and including the content item in current scope. - If you are rendering a custom last-crumb for your content you can set this to true to avoid double-crumbs for the current page. - - - - - - breadcrumb - blackboard.servlet.tags.ngui.BreadcrumbTag - JSP - Navigation item for the breadcrumb bar. - - href - false - true - The optional attribute setting the link for the element. - - - target - false - true - - The target attribute for the links in this breadcrumb, defaults to none. - - - - title - false - true - - The title for the crumb. If this attribute is not specified, the body of the tag - will be used as the title. - - - - - - viewToggle - blackboard.servlet.tags.ngui.ViewToggleTag - JSP - - -
  • - Global View Toggle - shows in the breadcrumb area, switches view for the entire page -
  • -
  • - Tool-specific Toggle - shows in the page header area, switches view for the specific tool itself -
  • - - -

    Example usage:

    - - - <bbNG:genericPage>
    -
    -    - <bbNG:pageHeader>
    - -      - <bbNG:pageTitleBar title="Discussion Board"/>
    - -      - <bbNG:breadcrumbBar >
    - -        - <!-- global view toggle (inside breadcrumbBar tag) -->
    - -        - <bbNG:viewToggle>
    -          - <bbNG:viewToggleItem id="pageToggle1" title="Page Toggle 1" href="javascript:doThis();" isActive="${isPageToggle1Active}" />
    -          - <bbNG:viewToggleItem id="pageToggle2" title="Page Toggle 2" href="javascript:goHere()" isActive="${!isPageToggle1Active}"/>
    -        - </bbNG:viewToggle>
    - -      - </bbNG:breadcrumbBar >
    -
    - -      - <!-- tool-specific toggle (inside pageHeader tag) -->
    - -      - <bbNG:viewToggle>
    -        - <bbNG:viewToggleItem id="toolToggle1" title="Tool Toggle 1" href="javascript:doSomething();" isActive="${isToolToggle1Active}" />
    -        - <bbNG:viewToggleItem id="toolToggle2" title="Tool Toggle 2" href="javascript:doSomethingElse();" isActive="${!isToolToggle1Active}" />
    -      - </bbNG:viewToggle>
    -
    -    - </bbNG:pageHeader>
    -
    -
    -    - <!-- start body -->
    -      - ...
    -    - <!-- end body -->
    -
    - </bbNG:genericPage> -
    - - ]]> -
    -
    - - - viewToggleItem - blackboard.servlet.tags.ngui.ViewToggleItemTag - JSP - A link in the view toggle button bar - - title - true - true - - The title of the link. - - - - id - false - true - - The css id of the link. - - - - href - false - true - - The location that the link will go to. - - - - onClick - false - true - - The onClick javascript handler for the link. - - - - isActive - false - true - - Whether the item is in the "active" state. Default is false. - - - - isHelpToggle - false - true - - Whether the item is the one with the "toggle page help" icon. Default is false. - - There must be maximum one view toggle item with this set to true. It will always be - the last view toggle item shown. - - - - hasEndBorder - false - true - - Whether to add a border at the end of the item. - - - - textFlow - false - true - - Direction of text flow. Value is either "natural" or "inverse" to account for RTL. For example, - if locale is left-to-right, "natural" would translate to "left" and "inverse" to "right". - - - - - - editModeToggle - blackboard.servlet.tags.ngui.EditModeToggleTag - JSP - A widget that allows toggling between read and edit modes - - editHref - true - true - - The location that the link will go to in edit mode. - - - - readHref - true - true - - The location that the link will go to in read mode. - - - - altEntitlement - false - true - - An alternate entitlement check that will be checked for rendering this tag. - - - - showToggle - false - true - - An alternate condition check that will be checked for rendering this tag.it can be true/false/null - The default value is null, it means this value is not set, and can be ignore. - True means always show this toggle no matter other condition and/or entitlement check result - False means do not show this toggle no matter other condition and/or entitlement check result - - - - - - contextMenu - blackboard.servlet.tags.ngui.ContextMenuTag - scriptless - - - Ordering context menu items -

    - There are 2 ways to give ordering to context menu items: -

      -
    1. - Give the order key from the context menu properties file - to the orderKey attribute. -
    2. -
    3. Give the order string to the order attribute.
    4. -
    -

    - There are currently 2 ways context menu items are added to the page. - Here is how to get the order string for each type of item: -

      -
    1. - Dynamically generated nav items for context menu items - - order is referenced by the internal handle of the nav item -
    2. -
    3. - Manually specified context menu items in jsp via contextMenuItem tags - - provide the id attribute for contextMenuItem tag OR - reference the default id string assigned to each context menu item (see TLD for - contextMenuItem tag for more information) -
    4. -
    -

    - Additional notes -

    -

      -
    • - Ids, if provided, for context menu items must be unique within each context menu. -
    • -
    • - If orderKey does not exist in the properties file, an error - will be logged to the default log file. -
    • -
    • - The context menu must have at least 1 item in it if not dynamically generated. -
    • -
    -

    - Example usage of context menu item ordering: -

    - - <bbData:constants type="blackboard.servlet.tags.ngui.ContextMenuTag"/>
    - <bbNG:contextMenu dynamic="true" - menuGeneratorURL="/webapps/blackboard/execute/modifyUserNavItemBasedContextMenuGenerator" - navItem="list_users" - contextParameters="xx" - order="user_properties,password_modify,${ContextMenuTag['SEPARATOR']},list_courses_by_user,manual_item1, - ${ContextMenuTag['DEFAULT_ITEM_ID_PREFIX']}1">
    -   <bbNG:contextMenuItem title="Manual item1" url="xx" id="manual_item1"/>
    -   <bbNG:contextMenuItem title="Manual item2" url="xx"/>
    - </bbNG:contextMenu>
    -
    -

    - The code will generate: -

    -

    -
    - User Properties
    - Change Password
    -
    -
    - Course Enrollments
    - Manual item1
    - Manual item2
    -
    -
    - [all other manual context menu items not referenced in order string]
    - [all other dynamically loaded context menu items not referenced in order string]
    -
    -
    - ]]> -
    - - menuLinkTitle - false - true - Text for the title attribute of the context menu link. If not specified will default to: Click to see options - - - dynamic - false - true - Whether this context menu is populated at run time. - - - menuGeneratorURL - false - true - Url that the dynamic options will be loaded from. - - - contextParameters - false - true - Context parameters needed for resolution of href's. - - - orderKey - false - true - - base/config/internal/contextMenuOrder.properties - file to load the context menu item order from. - ]]> - - - - order - false - true - - ContextMenuTag class for the separator and default item id strings. -

    - Example: -

    - <bbData:constants type="blackboard.servlet.tags.ngui.ContextMenuTag"/> -
    - <bbNG:listContextMenu order="sample_item1,${ContextMenuTag['SEPARATOR']},${ContextMenuTag['DEFAULT_ITEM_ID_PREFIX']}"...> - ]]> - - - - - - contextMenuItem - blackboard.servlet.tags.ngui.ContextMenuItemTag - empty - - - - - title - true - true - Title of the item. - - - url - true - true - URL that the item links to. - - - folder - false - true - The URL for the webDav folder. - - - id - false - true - - ContextMenuTag.DEFAULT_ITEM_ID_PREFIX + n, where "n" is 1,2,3,... - (based on the order this tag is specified in a context menu tag). - ]]> - - - - toolTip - false - true - Tool Tip text for the item. - - - onClick - false - true - - The onClick javascript handler for the link. - - - - target - false - true - - The target frame/window that the link will open in. Default is no target (the current window) - - - - overwriteNavItem - false - true - - Navigation item to be overwritten by this context menu item, when a menu is - dynamically generated based on navigation items. Navigation item whose internal handle - matches to this attribute will be excluded in dynamic menu generation and - this manual menu item will replace it. This attribute can be used to overwrite - label, url or other attributes of an auto-generated nav item based menu item. - - - - - - - - actionControlBar - blackboard.servlet.tags.ngui.actioncontrol.ActionControlBarTag - JSP - - Scaffolding tag for actionMenu, actionButton and actionPanelButton items. - There must be at least one action-type item in the action control bar. - Action menu items are aligned to the natural left and action panel button - items are aligned to the natural right. Action buttons are generated to - the natural left after action panel buttons. - - - showWhenEmpty - false - true - - Whether to draw the action bar when there are no items in it. This is useful for when items are dynamically added - to the action bar (such as with drag and drop accessible repositioning). - - Default: false - - - - - - actionMenu - blackboard.servlet.tags.ngui.actioncontrol.ActionMenuTag - scriptless - - Action menu is the container for a corresponding menu item list. - This tag is only for use within an action control bar tag. - An action menu must have at least one action menu item. - - - title - true - true - Action menu title. The menu title should be localized text. - - - menuItems - false - true - - List of menu items (blackboard.servlet.data.ngui.ActionMenuItem) with which to - populate the action menu. - - - - primary - false - true - - Boolean attribute indicating the type of the action menu. If true, the - menu is a primary button, otherwise, it is a secondary button. - Primary menus will show up in the order they are specified in - "natural left order" after action menus. Secondary menus will show up - in "natural right order", but to the left of any action panel buttons that are - specified. The default is true, meaning that the menu is a primary menu. - - - - - - actionMenuItem - blackboard.servlet.tags.ngui.actioncontrol.ActionMenuItemTag - empty - - Action menu items are items within an action menu. - This tag is only for use within an action menu tag. - - - title - true - true - - Action menu item title. The menu item title should be localized text. - - - - href - true - true - Url for the menu item. The URL where the item points. - - - target - false - true - - HTML link attribute to control which window the URL link is opened in. - - - - - - actionButton - blackboard.servlet.tags.ngui.actioncontrol.ActionButtonTag - empty - - Action buttons in the action control bar. - This tag is only for use within an action control bar tag. - - - title - true - true - Action button title. The title should be localized text. If an icon is specified, the title will be used as ALT - - - id - false - true - Action button id. unique value to identify this element - - - iconUrl - false - true - - URL of the icon to be displayed. If specified, the button will be the icon only, title being used for the alt. - - - - url - true - true - The URL where the button points. - - - target - false - true - - HTML link attribute to control which window the URL link is opened in. - - - - primary - false - true - - Boolean attribute indicating the type of the action button. If true, the - button is a primary button, otherwise, it is a secondary button. - Primary buttons will show up in the order they are specified in - "natural left order" after action menus. Secondary buttons will show up - in "natural right order", but to the left of any action panel buttons that are - specified. The default is false, meaning that the button is a secondary button. - - - - - - actionButtonCoursePicker - blackboard.servlet.tags.ngui.actioncontrol.ActionButtonCoursePickerTag - empty - - Coruse Picker in the action control bar. - This tag is only for use within an action control bar tag. - - - title - true - true - Action button title. The title should be localized text. If an icon is specified, the title will be used as ALT - - - id - false - true - Action button id. unique value to identify this element - - - iconUrl - false - true - - URL of the icon to be displayed. If specified, the button will be the icon only, title being used for the alt. - - - - formName - true - true - - The String associated with the form name attribute in the HTML,for example - ]]> - Used to reference form input elements via Javascript - - - - tfName - true - true - The form element name for the textfield should be defined in the JSP - - - tfValue - false - true - - The value for the textfield (optional, if not specified will be pulled off the - request) - - - - userId - false - true - - _x_y version of the userId that is specified when the selected courseId should - only be one for which the specified user has course admin rights. If no userId is - specified, all courses are allowed and will show up when browsing - - - - courseType - false - true - - Can be either "course" or "org". The default is "course". This attribute switches - the label appropriately and changes searching between course and organization - service level - - - - selectMultiple - false - true - Allows the user to select multiple courses - - - submitUrl - true - true - The URL for submit action. - - - primary - false - true - - Boolean attribute indicating the type of the action button. If true, the - button is a primary button, otherwise, it is a secondary button. - Primary buttons will show up in the order they are specified in - "natural left order" after action menus. Secondary buttons will show up - in "natural right order", but to the left of any action panel buttons that are - specified. The default is false, meaning that the button is a secondary button. - - - - - - actionMultiColumnFlyout - blackboard.servlet.tags.ngui.actioncontrol.ActionMultiColumnFlyoutTag - JSP - - Outputs a multi-column flyout menu on the Action Menu Control bar. - Must be enclosed by a SimpleActionControlBarComponentTag. - - - actionButtonLabel - true - true - - Localized action button label - - - - menuId - true - true - - Unique id of the multi column flyout menu - - - - - - actionMultiColumn - blackboard.servlet.tags.ngui.actioncontrol.ActionMultiColumnTag - JSP - - Adds a column to the ActionMultiColumn Flyout widget. - - - heading - true - true - - Localized column heading value - - - - items - false - true - - Column links represented by a list of ActionMenuItems - - - - itemsFromMap - false - true - - Column links represented by a map of ActionMenuItems - - - - - - actionMultiColumnSection - blackboard.servlet.tags.ngui.actioncontrol.ActionMultiColumnSectionTag - empty - - Column data in the MultiColumn Flyout can be organized into sections. - This tag allow Column links to be divided into a section. A section - in a column is separated by a vertical line. - This tag must be enclosed by the ActionMultiColumnTag. - - - items - false - true - - Section column links represented by a list of ActionMenuItems - - - - itemsFromMap - false - true - - Section column links represented by a map of ActionMenuItems - - - - - - webDavActionButton - blackboard.servlet.tags.ngui.actioncontrol.WebDavActionButtonTag - empty - - Action buttons in the action control bar that open a webdav folder. - These buttons are identical in appearance to actionButtons, but set the - folder attribute on the button link so that the button opens a webdav folder - instead of a web page. - This tag is only for use within an action control bar tag. - - - title - true - true - Action button title. The title should be localized text. If an icon is specified, the title will be used as ALT - - - id - false - true - Action button id. unique value to identify this element - - - iconUrl - false - true - - URL of the icon to be displayed. If specified, the button will be the icon only, title being used for the alt. - - - - url - true - true - The URL where the button points. - - - folder - false - true - The URL for the webDav folder. - - - target - false - true - - HTML link attribute to control which window the URL link is opened in. - - - - primary - false - true - - Boolean attribute indicating the type of the action button. If true, the - button is a primary button, otherwise, it is a secondary button. - Primary buttons will show up in the order they are specified in - "natural left order" after action menus. Secondary buttons will show up - in "natural right order", but to the left of any action panel buttons that are - specified. The default is false, meaning that the button is a secondary button. - - - - - - - actionPanelButton - blackboard.servlet.tags.ngui.actioncontrol.ActionPanelButtonTag - JSP - - - Use the alwaysOpen attribute if the action panel should always show - and no button should be rendered to toggle the panel. - ]]> - - - type - true - true - - The type of the action panel button to help determine the title of the button. - The type will take priority in determining the title of this button if both - attributes are provided. Allowed types are ALIGNMENTS, DISCOVER, FILTER, OTHER, and - SEARCH. If type is OTHER, then the title attribute must be provided. - - - - title - false - true - - Action panel button title. The title should be localized text. - - - - id - false - true - - panel$id). - ]]> - - - - alwaysOpen - false - true - - Setting for whether the action panel should always show, thus - no button should be rendered to toggle the panel. - Default is false. - - - - defaultOpen - false - true - - Setting for whether the action panel should open on default. - Default is false. - - - - panelDivClassOverride - false - true - - Optional override of the class attribute value of the panel div element. - By default, class values are provided. - - - - persistId - false - true - - - - - - - - discoverContentPanel - blackboard.servlet.tags.ngui.actioncontrol.ActionDiscoverContentPanelTag - empty - - The discover content panel tag allows content artifact selection via either an - Artifact type select box or directly enables the action panel button to be linked - to open a Artifact picker pop up. This tag is only for use within an action - panel button tag. - - - type - false - true - - Type of Artifacts to allow user to select from. Valid values are - "discussion_thread", "discussion_forum", "gradebook_item" and "all". - The default value is "all", where an Artifact type select box will be - displayed within the action panel when the action panel button is accessed. - Upon selecting an artifact, the user will be sent to the add page for that - particular type with the VTBE text pre-filled with the text of the - selected artifact. - - - - addPageQueryString - false - true - - Query string parameters needed for the add page of the learning object - specified in the type attribute. - - - - - - simpleActionControlBarComponent - blackboard.servlet.tags.ngui.SimpleActionControlBarComponentTag - JSP - true - - Simple wrapper to render its body content in a way similar to an actionPanelButton - on actionControlBar does. Unlike actionPanelButton that only renders a button - on the bar to control the content placed outside of the bar, the entire content - of this tag is rendered as is on the bar with a just thin wrapper around it. - Therefore it can be used to insert simple one off controls or input html, - such as navigation links or select element. - Tag should be used discretely to avoid rendering unreasonably big items on - actionControlBar. - - - renderFirst - false - true - - Whether to render this component at the front on the actionControlBar. If multiple - simpleActionControlBarComponents exist with this flag set to true, they will render in - the order they were defined in the body of actionControlBar. - - - - - - - - tabView - blackboard.servlet.tags.ngui.TabViewTag - scriptless - - Container for TabViewTab's - - - style - false - true - The style to use for the tab view container. - - - onTabChanged - false - true - Javascript handler for tab change, parameter passed are index and (if present) tabId . example: onTabChanged='initTabContent( tabId );' - - - - - tabViewTab - blackboard.servlet.tags.ngui.TabViewTabTag - scriptless - - A TabViewTab inside the TabView container. These tabs are client-side only. Clicking on them 'flips' - to another tab in the DOM. The tabs will be rendered in the order they are defined. If no active - tab is defined, the first tab in the set will be the active tab. - - - title - true - true - Title of the tab. - - - id - false - true - DOM id for the tab - - - active - false - true - - true if this tab is the active tab. If no active tab is defined, the first tab in the set will - be the active tab. - - - - - - - - pageTabs - blackboard.servlet.tags.ngui.PageTabsTag - JSP - - Container for secondary tabs at the top of a portal page. - - - - - pageTab - blackboard.servlet.tags.ngui.PageTabTag - scriptless - - A page tab in the page tabs section of the page. Any HTMl in the body of this tag - will be appended after the tab link (this is so that context menus, etc can be added) - - - title - true - true - Title of the tab. - - - url - true - true - Url that the tab links to - - - id - false - true - CSS id for the tab - - - onClick - false - true - Javascript that will be run when the tab link is clicked. - - - active - false - true - Whether this tab is the active one (default false). - - - personal - false - true - Whether this tab is a personal tab (default false). - - - - - pageHeaderTabs - blackboard.servlet.tags.ngui.PageHeaderTabsTag - JSP - - - - - - - pageHeaderTab - blackboard.servlet.tags.ngui.PageHeaderTabTag - scriptless - - A page tab in the page header of the page. Any HTML in the body of this tag - will be appended after the tab link (this is so that context menus, etc can be added) - - - title - true - true - Title of the tab. - - - url - true - true - Url that the tab links to - - - onClick - false - true - Javascript that will be run when the tab link is clicked. - - - active - false - true - Whether this tab is the active one (default false). - - - - - - moduleLayout - blackboard.servlet.tags.ngui.module.ModuleLayoutTag - JSP - - Container tag that encloses a page consisting of columns of "modules". - Can support drag and drop by specifying the "reorderable", "reorderingUrl", - and (optionally) "contextParameters" arguments. - - The layout currently only works with 2 or 3 columns (CSS restriction). - - - reorderable - false - true - Whether this module layout is reorderable (default false). - - - reorderingUrl - false - true - The url to which changes in the order of the modules will be - persisted to (using Ajax). - - - reorderType - false - true - A string describing the type of item being reordered (used as part of the title on the accessible repositioning controls). Required if reorderable. - - - displayControllerUrl - false - true - The url to which changes module display (e.g. max/min/close) will be - (using Ajax). - - - contextParameters - false - true - Query string style argument for passing additional parameters to - the reordering/displaycontrol server-side code in order to give some contextual - information. - - - numColumns - true - true - The number of columns in the layout - - - - - moduleColumn - blackboard.servlet.tags.ngui.module.ModuleColumnTag - JSP - - Represents a column in a modular layout. - - - - - module - blackboard.servlet.tags.ngui.module.ModuleTag - JSP - - Displays a "module" in a modular layout. The module has a title, and some options as to - whether the module has a settings page, can be minimized, or can be removed. - - The body of this tag will be placed into the body of the module. - - - id - true - true - Id that uniquely identifies this module on the server-side (used for repositioning). - - - extId - false - true - Id that identifies the external reference type of this module. Whatever value you pass here will be placed in an html comment inside the div for this module - - - title - true - true - The title of the module - - - suppressTitle - false - true - Whether the title (and modify buttons) should be suppressed. Default: false - - - settingsUrl - false - true - Url that the "settings" link will go to. Required if editable is set to true. - - - editable - false - true - Whether this module has a "settings" page. Default: false. - - - collapsible - false - true - Whether this module can be minimized. Default: true. - - - collapsed - false - true - Whether this module is initially minimized. Default: false. - - - removable - false - true - Whether this module can be removed. Default: true. - - - detachable - false - true - Whether this module can be detached (opened in a new window). Default: false. - - - detachUrl - false - true - Url that the detach popup contents will be set to. Required if detachable is set to true. - - - - - - searchBar - blackboard.servlet.tags.SearchBarTag - JSP - - This tag provides a simple search bar with a dropdown of attributes to search on and a - text field for doing a contains search. Include SearchOptionsTag in the Tag body - to set the options in the drop down. - - - searchLabel - false - true - - The search bar label to display. If not specified, defaults to action.search - bundle key in common.properties, which in en_US is "Search". - - - - selectName - false - true - - the name of the search by select. Default is "searchKey". - - - - selectValue - false - true - - the value of the search by select. - - - - textfieldName - false - true - - the name of the textfield input. Default is "searchValue". - - - - textfieldValue - false - true - - the value of the textfield input. - - - - operatorName - false - true - - the name of the operator select. Default is "searchOp". - - - - operatorValue - false - true - - the value of the operator select. - - - - operatorValue - false - true - - the value of the operator select. - - - - onClick - false - true - - The action to be hit when the "GO" button is clicked. caution: it always submit the surrounding form no matter specified or not. - - - - filterBoxStyle - false - true - - If anything other than "searchbar" is passed, the filter box will not be wrapped with a div at all. - - - - renderBodyContentInLine - false - true - - By default, any body content defined within the search bar tag (that is - non-search tag related) will be rendered below the output of the tag. - This rendering can be problematic and stylistically may differ from the - requirements. Setting this value to true, will instead render this body - content in line with (same vertical position) as the output of the tag. - - - - - - attrListSearchBar - blackboard.servlet.tags.AttrListSearchBarTag - JSP - - This tag provides a simple search bar with a dropdown of attributes to search on and a - text field for doing a contains search. - - - searchLabel - false - true - - The search bar label to display. If not specified, defaults to action.search - bundle key in common.properties, which in en_US is "Search". - - - - model - true - true - - AttributeListModel - - - - allowedRefColumns - false - true - - List of attribute names of String reference columns that are allowed to - appear in the search key drop down. - - - - onClick - false - true - - The action to be hit when the "GO" button is clicked. caution: it always submit the surrounding form no matter specified or not. - - - - - - searchOption - blackboard.servlet.tags.SearchOptionTag - JSP - - This tag is always used within an enclosing searchBar tag and it defines a selection - in the drop down of the search bar. - - - label - true - true - - Label of the option - - - - value - true - true - - the value of the option - - - - - - - - - form - blackboard.servlet.tags.ngui.FormTag - JSP - true - - - - - isSecure - false - true - - Optional, default false. If this is specified, then additional security measures will be added to the form. For instance, XSRF countermeasure. - - - - nonceId - false - true - - isSecure is not specified or set to false. - It is mandatory if the isSecure is set to true. - ]]> - - - - - - dataCollection - blackboard.servlet.tags.ngui.datacollection.DataCollectionTag - JSP - - Scaffolding tag for a data collection page. - A data collection page must contain at least one step and step submit. - Generally, only steps and step submit tags are used inside a data collection - tag. Other body content (eg. hidden form input fields) is rendered below - the steps and step submit. - - - markUnsavedChanges - false - true - - Whether to visually indicate data elements which have changed from their default value. Default false. - - - - showSubmitButtons - false - true - - Whether or not the step submit/cancel buttons should be shown. Default true. - If it is false it will not render the top/bottom cancel/submit buttons on the data collection page. - - - - hasRequiredFields - false - true - - Whether the data collection should show the information about required fields. Generally, this will - automatically be set to the correct value by the various tags used inside the data collection, but - sometimes it must be forced. For those occasions, use this attribute. - - - - - - stepGroup - blackboard.servlet.tags.ngui.datacollection.StepGroupTag - JSP - - A logical group of steps on a data collection page. A dataCollection with more - than one step group will cause tabs to appear at the top of the dataCollection - which will allow the user two switch between the different areas of the - page. - - If steps tags are located outside of a step group, they will all be added to the - end of the first step group. If there is only one step group, no tabs will be - shown. If no group is set as active, the first group will be active. If more than - one group is set as active, the one that comes first will be the active one. - - - title - true - true - - Localized title for the step group. - - - - active - false - true - Whether this group is the active one. Default false. - - - - - step - blackboard.servlet.tags.ngui.datacollection.StepTag - JSP - - - Manipulating the visibility of steps via javascript: -

    - If you wish to show or hide steps via javascript, simply provide the id - and call the following convenience method: page.steps.hideShowAndRenumber(action, stepIdArr). - The method can hide or show an array of steps given the string ids. - See page.js for full method documentation. -

    - Example: -

    - - <script> -
    - ... -
    - // hide the steps "infoStep","taskStatusStep", and "linkedContentStep" on the page
    - page.steps.hideShowAndRenumber(page.steps.HIDE, ["infoStep","taskStatusStep","linkedContentStep"] ); -
    - ... -
    - // show the step "infoStep" on the page
    - page.steps.hideShowAndRenumber(page.steps.SHOW, ["infoStep"] ); -
    - ... -
    - </script> -
    - ]]> - - - id - false - true - - Id used for the top containing element produced by this tag. - The default is "step[step_number]", for example, "step1". - If provided, it must be unique across all elements on the page. - - - - title - true - true - The localized text title of the step. - - - instructions - false - true - The localized text displayed as the step instructions. - - - type - false - true - - The type of step that should be rendered. Currently, a step can be of type - "data_collection" or "information". Information steps are visually presented - with a shaded background. This value must be the string representation - of one of the StepTag.Type enum values. - The default value: data_collection (Type.data_collection). - - - - oppositeSideHtml - false - true - - A block of html to render on the right hand side of the title line for this step. Note that it will - be placed at the end of the step as far as tab-order goes, even though it will be positioned in the title. - - - - hideNumber - false - true - - If hideNumber="true" then the step number will not be displayed - - - - optionalClass - false - true - - - For example: <bbNG:step title="title" optionalClass="tableOverflowControl">
    - will render "<div id="step1" class="tableOverflowControl"> - ]]> -
    -
    - - noFieldSet - false - true - - If noFieldSet="true" then the step 'field set' block will not be rendered - - - - - - stepInstructions - blackboard.servlet.tags.ngui.datacollection.StepInstructionsTag - empty - - instructions attribute on the step tag), but this tag may be used - for unusual cases. This tag may only be used within a step tag. - ]]> - - - text - true - true - - The text to be displayed as step instructions. The text should be localized text. - - - - - - stepSubtitle - blackboard.servlet.tags.ngui.datacollection.StepSubtitleTag - empty - - - - - text - true - true - - The text to be displayed as step instructions. The text should be localized text. - - - - - - stepSubmit - blackboard.servlet.tags.ngui.datacollection.StepSubmitTag - scriptless - - The step submit allows a user to submit data to a system or to cancel - that action. When used without any attributes or body, it will generate - a "Cancel" button as a secondary button and a "Submit" button as a primary button. - Optionally, one can specify step submit buttons to be generated. - - - title - false - true - - The localized title of the step. If not provided, it will default to - the label of the primary step submit button. - - - - instructions - false - true - - The localized text displayed as the step instructions. - The default is no instructions will be displayed. - - - - cancelUrl - false - true - - The URL to recall the page if the user cancels. - The default is the parent nav item's URL (if set and available) - or the javascript back function. - Only one of cancelUrl or cancelOnClick should be specified - - - - cancelOnClick - false - true - - The onClick value to recall the page if the user cancels. - The default is the parent nav item's URL (if set and available) - or the javascript back function. - Only one of cancelUrl or cancelOnClick should be specified - - - - showCancelButton - false - true - - Boolean indicating whether to show a "Cancel" button. - The default is true when no step submit buttons are specified, - or, when step submit buttons are specified AND the cancelUrl is - specified. Otherwise it will default to false. - - - - hideNumber - false - true - - If hideNumber="true" then the step number will not be displayed - - - - - - stepSubmitButton - blackboard.servlet.tags.ngui.GenericSubmitButtonTag - empty - - Step submit buttons can be specified within the step submit tag to override - the default buttons that appear with the step submit tag and display a select - number of buttons. When step submit buttons are specified, the first button - will be assumed to be primary (unless another button declares itself as primary - via the primary attribute) and displayed on the natural right of the page. Only - one button will be rendered as the primary button. If more than one button is set - as the primary button, the first one declared will be set as the primary button. - All remaining buttons are considered to be secondary, displayed to the natural left - of the primary button, in the order listed. - - - id - false - true - The id of the button - - - label - true - true - - The text on the button. - - - - url - false - true - - The URL used to link the button. Only one of url or onClick may be specified. - - - - onClick - false - true - - An onClick handler for the button. Only one of url or onClick may be specified. - - - - primary - false - true - - Boolean declaring a button as the primary submit button. - The default is false. - - - - - - genericSubmit - blackboard.servlet.tags.ngui.GenericSubmitTag - scriptless - - - Note: Generic submit tags should not be used within a data collection page. - For those pages, use the step submit tag instead. - ]]> - - - cancelUrl - false - true - - The URL to recall the page if the user cancels. - The default is the parent nav item's URL (if set and available) - or the javascript back function. - Only one of cancelUrl or cancelOnClick should be specified - - - - cancelOnClick - false - true - - The onClick value to recall the page if the user cancels. - The default is the parent nav item's URL (if set and available) - or the javascript back function. - Only one of cancelUrl or cancelOnClick should be specified - - - - showCancelButton - false - true - - Boolean indicating whether to show a "Cancel" button. - The default is true when no step submit buttons are specified, - or, when step submit buttons are specified AND the cancelUrl is - specified. Otherwise it will default to false. - - - - - - genericSubmitButton - blackboard.servlet.tags.ngui.GenericSubmitButtonTag - empty - - Generic submit buttons can be specified within the generic submit tag to override - the default buttons that appear with the submit tag and display a select - number of buttons. When submit buttons are specified, the first button - will be assumed to be primary (unless another button declares itself as primary - via the primary attribute) and displayed on the natural right of the page. Only - one button will be rendered as the primary button. If more than one button is set - as the primary button, the first one declared will be set as the primary button. - All remaining buttons are considered to be secondary, displayed to the natural left - of the primary button, in the order listed. - - - label - true - true - - The text on the button. - - - - url - false - true - - The URL used to link the button. Only one of url or onClick may be specified. - - - - onClick - false - true - - An onClick handler for the button. Only one of url or onClick may be specified. - - - - primary - false - true - - Boolean declaring a button as the primary submit button. - The default is false. - - - - - - textElement - blackboard.servlet.tags.ngui.datacollection.fields.TextElementTag - empty - true - - Text elements generate a form input type of text. If used inside a data collection page - step, it must be wrapped with the data element tag. - This tag accepts dynamic attributes in addition to the ones listed, for example, - attributes like event attributes (onblur, onclick and onchange) may be specified. - - - isRequired - false - true - - Boolean whether this element is required. - If required, there must be input in the text box (validated via javascript). - The default is false. - - - - helpText - false - true - - The help text for this element explaining or clarifying this field. - The help text should be localized text. - The default is no help text. - - - - displayOnly - false - true - - Boolean setting to show the display only view of this element. - If this element is display only, only the text value will be outputted, - the input box will not be outputted. The default is false. - - - - type - false - false - - TextElementTag.Type for the entire list - of proper values. Consider adding a new type if you need - validation that is generic enough for re-use. - ]]> - - - - maxLength - false - true - - The maximum number of characters a user can enter. - The default is an unlimited numbers. - - - - minLength - false - true - - The minimum number of characters a user must enter. - The default is no minimum when the element is not required, else, - if the element is required, the default minimum is 1 character. - The minimum length is enforced via JavaScript. - - - - size - false - true - - The initial width of this element. - This corresponds to the size attribute of the HTML - input tag. The default size is 20 characters. - - - - value - false - true - - The initial value of this element. This corresponds to the - value attribute of the HTML input tag. - - - - name - true - true - - The name of this form element. - This corresponds to the name attribute of the HTML input tag. - The name will be used for the id as well if id is not specified. - - - - id - false - true - - The id of this form element. This corresponds to the id attribute of the HTML - input tag. If not specified, the id will be the same as the specified name. - Id must be unique from all other form elements on the page. - - - - title - false - true - - The title corresponds to the title attribute of the HTML input tag (tool tip text). - For accessibility, the title text should be localized text describing the purpose - of the input text box. The title is also used in the javascript validation message - when validating text length requirements. - - - - isVertical - false - true - - Boolean attribute indicating whether this text box should render on its own line, - used when rendering a group of form elements in a vertical format. - The default is false. - - - - isDisabled - false - true - - Boolean attribute indicating whether this text box is initially - disabled. The default is false, meaning the text box is not initially - disabled. A disabled element must be enabled via Javascript before it - can be used on a page. - - - - isPassword - false - true - - Boolean attribute indicating whether this text box is a password field - The default is false, meaning the text box is a normal text field. If it is true, this field represents as a password field. - - - - - - checkboxElement - blackboard.servlet.tags.ngui.datacollection.fields.CheckboxElementTag - empty - true - - Check box elements generate a form input type of checkbox. If used inside a data collection - page step, it must be wrapped with the data element tag. - This tag accepts dynamic attributes in addition to the ones listed, for example, - attributes like checked and event attributes (onblur, onclick and onchange) - may be specified. - - - helpText - false - true - - The help text for this element explaining or clarifying this field. - The help text should be localized text. - The default is no help text. - - - - displayOnly - false - true - - Boolean setting to show the display only view of this element. - If true, the optionLabel text will be required and rendered and - the checkbox will not be outputted. The default is false. - - - - value - true - true - - The initial value of this element. - This corresponds to the value attribute of the HTML input tag. - - - - name - true - true - - The name of this form element. - This corresponds to the name attribute of the HTML input tag. - The name will be used for the id as well if id is not specified. - - - - id - false - true - - The id of this form element. - This corresponds to the id attribute of the HTML input tag. - If not specified, the id will be the same as the specified name. - Id must be unique from all other form elements on the page. - - - - title - false - true - - The title corresponds to the title attribute of the HTML input tag (tool tip text). - For accessibility, the title text should be localized text describing - the purpose of the check box. This may be the same text as the label - for the data element tag. The default is no title. - - - - optionLabel - false - true - - The localized text to be displayed next to the check box, describing it. - The default is no text next to the check box. - - - - isSelected - false - true - - Boolean attribute indicating whether this checkbox is initially checked. - The default is false, meaning the checkbox is not initially selected. - - - - isDisabled - false - true - - Boolean attribute indicating whether this checkbox is initially disabled. - The default is false, meaning the checkbox is not initially disabled. A - disabled element must be enabled via Javascript before it can be used on - a page. - - - - isVertical - false - true - - Boolean attribute indicating whether this checkbox should render on its own line, - used when rendering a group of checkboxes in a vertical format. - The default is false. - - - - - - radioElement - blackboard.servlet.tags.ngui.datacollection.fields.RadioElementTag - JSP - true - - Radio elements generate a form input type of radio. If used inside a data collection - page step, it must be wrapped with the data element tag. - This tag accepts dynamic attributes in addition to the ones listed, for example, - attributes like event attributes (onblur, onclick and onchange) may be specified. - - - helpText - false - true - - The help text for this element explaining or clarifying this field. - The help text should be localized text. The default is no help text. - - - - displayOnly - false - true - - Boolean setting to show the display only view of this element. - If true, the optionLabel text will be required and rendered and - the radio button will not be outputted. The default is false. - - - - value - true - true - - The initial value of this element. - This corresponds to the value attribute of the HTML input tag. - - - - name - true - true - - The name of this form element. - This corresponds to the name attribute of the HTML input tag. - The name will be used for the id as well if id is not specified. - - - - id - false - true - - The id of this form element. - This corresponds to the id attribute of the HTML input tag. - If not specified, the id will be the same as the specified name. - Id must be unique from all other form elements on the page. - - - - title - false - true - - The title corresponds to the title attribute of the HTML input tag (tool tip text). - For accessibility, the title text should be localized text describing - the purpose of the radio button. This may be the same text as the label - for the data element tag. The default is no title. - - - - optionLabel - false - true - - The localized text to be displayed next to the radio button, describing it. - The default is no text next to the check box. - - - - isSelected - false - true - - Boolean attribute indicating whether this radio button is initially selected. - The default is false, meaning the radio button is not initially selected. - - - - isDisabled - false - true - - Boolean attribute indicating whether this radio is initially disabled. - The default is false, meaning the radio is not initially disabled. A - disabled element must be enabled via Javascript before it can be used on - a page. - - - - isVertical - false - true - - Boolean attribute indicating whether this radio button should render on its own line, - used when rendering a group of radios in a vertical format. - The default is false. - - - - - - dataElement - blackboard.servlet.tags.ngui.datacollection.DataElementTag - JSP - - textElement, selectElement, radioElement, - checkboxElement, etc ). -

    - Help text for manual HTML form elements defined within this tag may be provided - via the elementInstructions tag. - It can optionally contain a delegateContextMenu tag which will render a - context menu next to the label of the item. - ]]> - - - label - false - true - - The label for this element. The label should be localized text. - The default is no label. - - - - isRequired - false - true - - Boolean attribute indicating whether contents of this element are required. - The default is false. - If required, validation should be coded for the contents of the data element - to meet what is acceptable user entry. Many of the NG element tags have built-in - validation. See their TLD doc for details. - - - - labelFor - false - true - - - Assign a form element id to this attribute to associate the label text of - this data element to a specific form control element. -

    - For example: -
    - <label for="my_button">Fun Button</label> -
    - <input type="image" id="my_button" src="blue_button.gif"> -

    - If specified, a value must also be provided for the 'label' attribute. - ]]> - - - - id - false - true - - Id used for the top containing element produced by this tag. - - - - isSubElement - false - true - - Whether this data element tag either is a sub element (is contained within a parent dataElement tag) - or has sub elements (contains other dataElement tags). In order to use this properly, both the parent - and child dataElement tags have to specify this attribute and set it to true. - - - - subElementType - false - true - - DataElementTag.SubElementType enum items. -

    - Default is NESTED_LIST. -

    - This attribute needs to be specified on the child dataElement; - setting it on the containing (parent) dataElement will not have - the desired effect. - ]]> - - - - isVertical - false - true - - Whether the body of this data element should be below the label. Default is false. - - - - renderLegendAndFieldset - false - true - - Render a legend and fieldset around this data element. - If true, a value must also be provided for one of the 'label' or 'legend' attributes. - Default is false. - - - - legend - false - true - - Specifies the text to use when rendering the hidden <legend> element (see 'renderLegendAndFieldset'). - Default is to use the "label" attribute's value. - - - - listItemClass - false - true - - for this row - ]]> - - - - - - elementInstructions - blackboard.servlet.tags.ngui.datacollection.fields.ElementInstructionsTag - empty - - dataElement - tag when creating a container of manually constructed data element fields. - Otherwise, use the standard data element tags that exist and their helpText attributes. - ]]> - - - text - true - true - - The text to be displayed as inline help text. The text should be localized text. - - - - - - selectElement - blackboard.servlet.tags.ngui.datacollection.fields.SelectElementTag - JSP - true - - Select elements generate a form select dropdown or scroll list box. - If used inside a data collection page step, it must be wrapped with the - data element tag. - The select element must contain at least one select option element. - This tag accepts dynamic attributes in addition to the ones listed, for example, - attributes like event attributes (onblur, onclick and onchange) may be specified. - - - isRequired - false - true - - Boolean attribute indicating whether this element is required. - If required, a value must be selected (validated via javascript). - The default is false. - - - - helpText - false - true - - The help text for this element explaining or clarifying this field. - The help text should be localized text. The default is no help text. - - - - displayOnly - false - true - - Boolean setting to show the display only view of this element. - If this element is display only, the text label(s) of the selected option(s) - will be outputted, the select dropdown or box will not be outputted. - The default is false. - - - - name - true - true - - The name of this form element. This corresponds to the name attribute - of the HTML input tag. The name will be used for the id as well if - id is not specified. - - - - id - false - true - - The id of this form element. This corresponds to the id attribute - of the HTML input tag. If not specified, the id will be the same - as the specified name. - Id must be unique from all other form elements on the page. - - - - title - false - true - - The title corresponds to the title attribute of the HTML select tag (tool tip text). - For accessibility, the title text should be localized text describing - the purpose of the input text box. - - - - multiple - false - true - - Boolean attribute to allow multiple selections. The default is false, - which means that the only one option may be selected at the same time. - - - - isDisabled - false - true - - Boolean attribute indicating whether this select element is initially - disabled. The default is false, meaning the select is not initially - disabled. A disabled element must be enabled via Javascript before it - can be used on a page. - - - - size - false - true - - The number of rows in the list that should be visible at the same time - if the select element is presented as a scrolled list box.This corresponds - to the size attribute of the HTML select tag. The default is 1. - - - - - - selectOptionElement - blackboard.servlet.tags.ngui.datacollection.fields.SelectOptionElementTag - empty - true - - Select options are the items in a select element. - This tag must be used within a select element tag. - This tag accepts dynamic attributes in addition to the ones listed, - for example, attributes like event attributes (onblur, onclick and - onchange) may be specified. - - - value - true - true - - The initial value of this element. - This corresponds to the value attribute of the HTML option tag. - - - - isSelected - false - true - - Boolean attribute indicating whether the option is initially selected. - The default is false. - - - - optionLabel - false - true - - The user displayed text of the option. - The option label text should be localized text. - Default is empty text. - - - - - - multiSelect - blackboard.servlet.tags.ngui.datacollection.fields.MultiSelectTag - JSP - - blackboard.servlet.data.MultiSelectBean as the types in the - collections passed to the source and destination select boxes. Upon form - submission, there will be comma-delimited string values of all options that were in the - source and destination select boxes for manipulation. -

    - Manipulating the multi-select widget via javascript: -

    - If you wish to manipulate the contents of the multi-select boxes with javascript, - there are convenience methods to do so in widget.js. The array of multi-select - javascript prototype objects on a page are available via this global variable: - widget.MultiSelect.multiselectBoxes. There are methods that allow adding - and resetting elements in the source and destination select boxes. The full list of - object methods are defined in widget.MultiSelect.prototype - in widget.js. - ]]> - - - helpText - false - true - - The help text for this element explaining or clarifying this element. - The help text should be localized text. The default is no help text. - - - - displayOnly - false - true - - Boolean setting to show the display only view of this element. If this element is display only, - only the text of the currently selected items (items in the "destination" collection) will - be outputted. The default is false. - - - - formName - true - true - - The name of the form (HTML name attribute of the form tag) where the multi-select widget - is being used. - - - - sourceName - false - true - - The name of the source select box element. This corresponds to the name attribute of the - HTML select tag. The name is also used as the id attribute. The default sourceName is - "[widgetName]_source_select", where widgetName is the given widgetName attribute, - or "multiselect" by default. - - - - destName - false - true - - The name of the destination select box element. This corresponds to the name attribute of the - HTML select tag. The name is also used as the id attribute. The default destName is - "[widgetName]_dest_select", where widgetName is the given widgetName attribute, - or "multiselect" by default. - - - - widgetName - false - true - - The name of the widget that will be prepended to the names of the source and destination - name attributes to ensure a more unique input name attribute. - The default widget name is "multiselect". - - - - sourceTitle - false - true - - The source title is the title that appears over the source select box. The source title - should be a localized text describing what is contained in the source list, for example, - "Roles to Select". Default value is "Items to Select". - - - - destTitle - false - true - - The destination title is the title that appears over the destination select box. - The destination title should be a localized text describing what is contained in the - destination list, for example, "Selected Roles". Default value is "Selected Items". - - - - sourceCollection - false - true - - The collection passed to the source select box. This is a list of MultiSelectBean objects - passed to be displayed on the source select box. The label and value attributes should be - set for each member of the list. The isSelected attribute can be set in case some options - need to be initially selected. - - - - destCollection - false - true - - The collection passed to the destination select box. This is a list of MultiSelectBean objects - passed to be displayed on the destination select box. The label and value attributes should be - set for each member of the list. The isSelected attribute can be set in case some options - need to be initially selected. - - - - size - false - true - - Size refers to the number of rows displayed in the select boxes. The size attribute is used for - both boxes. The default size is 8. - - - - minSelected - false - true - - The minimum number of items that must be selected, transferred from the source to the - destination boxes. The default is 0, meaning there is no minimum. The minimum number of - selected items check is enforced via JavaScript. - - - - maxSelected - false - true - - The maximum number of items that may be selected, transferred from the source to the - destination boxes. The default is 0, meaning there is no maximum. The maximum number of - selected items check is enforced via JavaScript. - - - - emptyMsg - false - true - - The localized text message to be displayed if both the source and destination collections are empty. - The default message is "No items for [soureTitle] and [destTitle] are found." - - - - showEmpty - false - false - - The showEmpty when true will display multiselect even if the left and right boxes are empty. - default value is false - - - - - - multiSelectAction - blackboard.servlet.tags.ngui.datacollection.fields.MultiSelectActionTag - JSP - - Performs the various actions on the selection options in the multiselect widget - ]]> - If there are 3 or less actions, then they will be sidplayed as buttons. In case - there are more than three options then it will be displayed as a dropdown - - - label - true - true - - This is the text that will appear on the button or dropdown for that action - - - - javaScriptFunc - true - true - - This is the name of the javascript function which the user has to write to perform - a certain action. The name of the function needs to be passed in as a attribute of - the tag. This javascript function is called with one parameter, which is the name - of the select Box which was passed in outer multiSelect tag - - - - value - false - true - The html "value" attribute for the option tag - - - - - colorPicker - blackboard.servlet.tags.ngui.picker.ColorPickerTag - empty - - Color picker is used for choosing color that generates a color swatch - and a color chooser button that will pop up a table of colors to choose from. - If used inside a data collection page step, it must be wrapped with the data element tag. - - - isRequired - false - true - - Boolean whether this element is required. - The default is false. - - - - helpText - false - true - - The help text for this element explaining or clarifying this field. - The help text should be localized text. The default is no help text. - - - - displayOnly - false - true - - Boolean setting to show the display only view of this element. - If this element is display only, only current selected color will be outputted, - the user will not be able to select another color. The default is false. - - - - name - true - true - - The name of this form element. - This corresponds to the name attribute of the HTML input tag. - - - - initialColor - false - true - - The initial color that is displayed for the color picker. The default is black (#000000). - - - - previewContainerId - false - true - - An optional preview container element ID string may be specified to - have the color picker widget change the color of the contents inside - the container when a color is chosen. - An example usage: for an on-screen and on-change font and color preview panel. - - - - previewBackgroundContainerId - false - true - - An optional preview background container element ID string may be specified to - have the color picker widget change the background color of the contents inside - the container when a color is chosen. - An example usage: for an on-screen and on-change font and color preview panel. - - - - - - colorPalettePicker - blackboard.servlet.tags.ngui.picker.ColorPalettePickerTag - empty - - Color palette picker is used for a choosing color palettes. It generates a sample color palette box - for the currently selected palette. There is a popup table of palettes to choose from. - If used inside a data collection page step, it must be wrapped with the data element tag. - - - name - true - true - - The name of this form element. - This corresponds to the hidden form element whose value is the current palette extRef - - - - value - false - true - - This corresponds to the current palette extRef. - The default is '' (empty string) for current brand system palette. - - - - showDownloadButton - false - true - - Whether to show a "Download Copy" button next to the currently selected palette. - Default false. - - - - onChange - false - true - - Javascript to run when the color palette is changed. - - - - - - textbox - blackboard.servlet.tags.ngui.datacollection.fields.TextboxTag - JSP - - Textbox generates a plain or wysiwyg textbox. If used inside a data collection page step, - it must be wrapped with the data element tag. - - - helpText - false - true - - The help text for this element explaining or clarifying this field. - The help text should be localized text. - The default is no help text. - - - - name - true - true - - - NOTE: The id attribute will be set to the same value as the name attribute. -

    - For example, given name="otherInfo", the following HTML will be generated: -
    - - <textarea name="otherInfotext" id="otherInfotext" ...> - - ]]> - - - - label - false - true - The label for this textbox. The label should be localized text. If not specified and this is inside a dataelement tag then the label from the dataelement tag will be used as the label for this element. - - - isMathML - false - true - - Whether WebEQ and MathML be available on this textbox, if enabled on the system. - Default: True - - - - isSpellcheck - false - true - - Whether spellcheck be available on this textbox, if enabled on the system. - Default: True - - - - isSpellcheckOnly - false - true - - Whether only the spellcheck button should appear in the VTBE (which will appear as - the 'legacy' VTBE, not wysiwyg). Default: False - - - - isUrlLink - false - true - - Whether the Insert File|Image|etc popup window should also allow linking to any - given URL. Default: False - - - - isFormattedText - false - true - - Whether the legacy textbox should include radio buttons for smarttext, plaintext, - and html. Default: True - - - - mode - false - true - deprecated]]> - - - rows - false - true - - The number of rows specified, with a default of 12 rows, and a minimum of 1 row. - - When the WYSIWYG textbox editor is turned on, the number of rows will affect the height of the - editor. - - - - cols - false - true - - As of 9.1, this setting no longer has any effect: the textbox will take up the entire available width - regardless of "Visual" mode or not. - - - - minLength - false - true - - The minimum length validated for (e.g., "Description must be at least 100 - characters long") - - - - maxLength - false - true - - The maximum length validated for (e.g., "Description cannot be more than 4000 - characters long") - - - - text - false - true - - The text string to be displayed in the textbox (generally used on Modify - operations) If a FormattedText object is passed, the format flag should NOT be - used. If a String is passed, the format flag should also be used, or the format - will default to SmartText. Default: null - - - - ftext - false - true - blackboard.base.FormattedText - - New code should use this attribute instead of passing a FormattedText object to the - text attribute. The old way will still work (for backward compatibility), but using - the ftext attribute is cleaner and avoids some problems jstl was having. - The text string to be displayed in the textbox. As above, if this is set, the format - flag should NOT be used. Default: null - - - - xContent - false - true - deprecated]]> - - - format - false - true - - Supported values: TextboxTag.HTML, TextboxTag.PLAIN_TEXT, or - TextboxTag.SMART_TEXT. Default: SMART_TEXT - - - - fileLocation - false - true - blackboard.servlet.data.FileLocation - - FileLocation object (object implements FileLocation interface) Default: null - - - - isLocalOnly - false - true - - Whether users may select existing files from the CS store or must upload files from their local machine. isLocalOnly and isCSOnly can not both be true. - Default: false. - - - - isCSOnly - false - true - - Whether users may upload files or must select existing files from the CS store. isLocalOnly and isCSOnly can not both be true. - Default: false. - - - - principalId - false - true - - A String representing the Content System permissions for files linked to this - textbox - - - - isRow2Collapsed - false - true - - Whether the second row of the VTBE is collapsed on page load, showing a control - arrow instead of the row 2 buttons. Default: False - - - - isRow3Collapsed - false - true - - Whether the third row of the VTBE is collapsed on page load, showing a control - arrow instead of the row 3 buttons. Default: False - - - - isStandalonePage - false - true - - Whether the resulting code from the wysiwyg object is a full standalone HTML page, - including and tags (usage: inside the content system). Default: False - - - - isContentLinking - false - true - - Whether the ability to browse the content system is desired for this textbox. - Default: False - - - - alwaysShowMashups - false - true - - Whether the mashups button should be shown in Wysiwyg mode regardless of whether - the third-row is disabled. - - Default: False - - - - currentFileId - false - true - - Id of the file the user is editing - - - - showArtifactVTBEFooter - false - true - - Whether to display the VTBE Artifact footer. The footer allows the user - to specify whether to save files as artifacts. - - - - saveAsArtifactType - false - true - - This stores the artifact type if the content is stored as artifact. - - - - copyFileId - false - true - - This stores the artifact type if the content is stored as artifact. - - - - displayCharacterCounter - false - true - - - If you wish to manipulate the contents of the character counter with javascript, - there are convenience methods to do so in widget.js. The array of multi-select - javascript prototype objects on a page are available via this global variable: - widget.TextBoxCounter.counters. Especially useful is a function - (update) that prompts the counter to update itself, based on the - current contents of its textbox. The full list of object methods are defined in - widget.TextBoxCounter.prototype in widget.js. - - ]]> - - - - allowSaveAsArtifactForNoCSLicense - false - true - - Whether showing the Save as RO at the bottom of the textbox. - Default: False - - - - - - textboxToolbarButton - blackboard.servlet.tags.ngui.datacollection.fields.TextboxToolbarButtonTag - empty - - Appends a new toolbar button to the VTBE editor. This currently supports showing a popup for the button's action. - - - tooltip - true - true - Button tooltip - - - imagePath - true - true - Path to the display image for the button - - - popupUrl - true - true - URL to open when the button is activated. - - - cssClass - false - true - Css class to apply to the button. Defaults to "button" - - - alwaysDisabled - false - true - java.lang.Boolean - Boolean indicating that the button is disabled. Defaults to false. - - - - - formattedText - blackboard.servlet.tags.FormattedTextTag - empty - - Widget that takes a FormattedText object and a FileLocation (optionally) and renders - text that has been appropriately parsed for UI display. - - - formattedText - true - true - blackboard.base.FormattedText - - The formattedText object that will be parsed for appropriate rendering - - - - fileLocation - false - true - The FileLocation object that is used to parse the @X@ tags to render links to uploaded objects. - - - - - datePicker - blackboard.servlet.tags.ngui.picker.DatePickerTag - empty - - - The actual form field that contain data that can be used by the action code - is: [baseFieldName]_datetime. The field is a string of - the date and or time chosen in an internal date time format to the date picker. -

    - Use the method in blackboard.servlet.util.DatePickerUtil to parse - the date time string from the picker into a java.util.Calendar object. - ]]> - - - helpText - false - true - - The help text for this element explaining or clarifying this field. - The help text should be localized text. - The default is no help text. - - - - isRequired - false - true - - Boolean whether this element is required and its input(s) cannot be empty. - The default is false. - - - - baseFieldName - false - true - - - - - - dateTimeValue - false - true - - null may be passed. - If the picker is required and null is passed, the date time value will - be set to default. The default is the current date and time. - ]]> - - - - showDate - false - true - - Optional setting to decide whether to show the date field. Default value is true. - - - - showTime - false - true - - Optional setting to decide whether to show the time field Default value is false. - - - - filterFutureYears - false - true - - Optional setting to decide whether to restrict the selection of the date and or time to - less than or equal to current date and or time. Default value is false. - - - - suppressInstructions - false - true - - Optional setting to suppress the default instruction text that appears beside the date fields. - Default is false. - - - - displayOnly - false - true - - If displayOnly=true then the input fields will be disabled. The default is false. - - - - midnightWarning - false - true - - If specified and the user chooses 'midnight' from the list then this message will be - displayed in an alert dialog box. - - - - defaultTimeOfDay - false - true - - - - - - externalValidateJsCallback - false - true - - - Example usage found in edit_announcement.jsp:
    - function announcementValidate ( dateType, pickerType, dateObjStr )
    -

    - ]]> -
    -
    -
    - - - - - dateRangePicker - blackboard.servlet.tags.ngui.picker.DateRangePickerTag - empty - - - The actual form fields that contain data that can be used by the action code - are: -
      -
    • [baseFieldName]_start_checkbox
    • -
    • [baseFieldName]_start_datetime
    • -
    • [baseFieldName]_end_checkbox
    • -
    • [baseFieldName]_end_datetime
    • -
    - The date time field is a string of the date and or time chosen in an internal - date time format to the date picker. -

    - Use the methods in blackboard.servlet.util.DatePickerUtil to parse - the date time string from the picker into a java.util.Calendar object, - and to determine whether a checkbox is checked. - ]]> - - - helpText - false - true - - The help text for this element explaining or clarifying this field. - The help text should be localized text. - The default is no help text. - - - - isRequired - false - true - - showStartCheckbox=true and or showEndCheckbox=true. - The default is false. - ]]> - - - - baseFieldName - false - true - - Base name of the picker fields in the form. - The default value is "bbDateTimePicker". - If there are more than one date or date range pickers per page, - then mandatory unique naming is required for the baseFieldName. - - - - startDateTime - false - true - - null may be passed. - If the picker is required and null is passed, the start date time value will - be set to default. The default is the current date and time. - ]]> - - - - endDateTime - false - true - - null may be passed. - If the picker is required and null is passed, the end date time value will - be set to default. The default is the current date and time. - ]]> - - - - showDate - false - true - - Optional setting to decide whether to show the date field for both - the start and end dates. Default value is true. - - - - showTime - false - true - - Optional setting to decide whether to show the time field for both - the start and end dates. Default value is false. - - - - showStartCheckbox - false - true - - Optional setting to decide whether a checkbox should be rendered associated with the - start date/time field(s). Default value is true. - If false, the start date time input is required cannot be empty. - - - - showEndCheckbox - false - true - - Optional setting to decide whether a checkbox should be rendered associated with the - end date/time field(s). Default value is true. - If false, the end date time input is required cannot be empty. - - - - startCaption - false - true - - Optional localized text string associated with the start check box. - Default text that is displayed: "Display Until". - - - - endCaption - false - true - - Optional localized text string associated with the end check box. - Default text that is displayed: "Display After". - - - - uncheckCheckboxes - false - true - - Optional setting to decide whether both the start and end date/time checkboxes - should default to the off (unchecked) state even if the start and or end dates - have been passed in. Default value is false. - - - - uncheckStartCheckbox - false - true - - Optional setting to decide whether the start date/time checkbox - should default to the off (unchecked) state even if the start date - has been passed in. Default value is false. - - - - uncheckEndCheckbox - false - true - - Optional setting to decide whether the end date/time checkbox - should default to the off (unchecked) state even if the end date - has been passed in. Default value is false. - - - - filterFutureYears - false - true - - Optional setting to decide whether to restrict the selection of the date and or time to - less than or equal to the current date and or time. Default value is false. - - - - suppressInstructions - false - true - - Optional setting to suppress the all of default instruction text that appears beside the date fields. - Default is false. - - - - suppressEndInstructions - false - true - - Optional setting to suppress the only the default instruction text that appears beside the end date field. - Default is false. - - - - externalValidateJsCallback - false - true - - - Example usage found in edit_announcement.jsp:
    - function announcementValidate ( dateType, pickerType, dateObjStr )
    -

    - ]]> -
    - -
    - - - formatDate - blackboard.servlet.tags.FormatDateTag - empty - - Format a date based on Bb standards - - - date - false - true - java.util.Date - - The date to format. At least this or calendar is required - - - - calendar - false - true - java.util.Calendar - - The calendar to format/ At least this of date is required - - - - showTime - false - true - Should the time be included in the rendering? - - - showDate - false - true - Should the date be included in the rendering? - - - dateStyle - false - true - - The style used to display the date - - - - timeStyle - false - true - - The style used to display the time - - - - - - filePicker - - blackboard.servlet.tags.ngui.picker.FilePickerTag - - blackboard.servlet.tags.ngui.picker.BaseListBasedPickerTei - JSP - - Manipulating the filePicker widget via javascript: -

    - If you wish to manipulate the filePicker widget with javascript, - there are convenience methods to do so in widget.js. filePicker - javascript prototype objects on a page can be accessed via the method - widget.FilePicker.getFilePicker(baseElementName) where baseElementName - is the value passed into the tag for the attribute of the same name. The full list of - methods accessible on the returned object are defined in widget.FilePicker.prototype - in the file widget.js. -

    - ]]> -
    - - mode - false - true - - -
  • LOCAL_FILE_ONLY - only pick local files
  • -
  • CS_FILE_ONLY - only pick CS files
  • -
  • CS_FILE_OR_URL - picker either CS files for URLs
  • -
  • LOCAL_OR_CS_FILE - pick either local or CS files ( default )
  • -
  • LOCAL_OR_CS_OR_URL - pieck, local, CS files, or URLs
  • -
  • INLINE_SINGLE_LOCAL_FILE_ONLY - choose a single local file (shown inline, doesn't create extra data elements)
  • -
  • INLINE_SINGLE_CS_FILE_ONLY - choose a single CS file (shown with input data element and CS file browse button.)
  • - - ]]> -
    -
    - - baseElementName - true - true - - The base name for the picker elements. - The local picker element will be this name appended with "_LocalFile" - The CS picker element will be this name appended with "_CSFile" - - - - required - false - true - - Whether a file is required to be picked. - - - - allowMultipleFiles - false - true - java.lang.Boolean - - True if this picker is used with an "Add Another File" button false if this picker - can only be used to choose one file (default: false) - - - - attachedFiles - false - true - java.util.List - A list of attached files/links. File objects must conform to the FilePickerFile interface. - - - var - true - false - Variable to expose the current attached file to in the picker list elements. - - - overrideLocalBehavior - false - true - java.lang.Boolean - Enables CS override of local behavior. - - - showLinkTitle - false - true - Whether an option to specify the link name of the file should be provided. Default true. - - - showSpecialAction - false - true - Whether a 'special actions' field of the picker should be provided. Default false. - - - showAddMetadata - false - true - Whether the "Submit and add metadata" functionality should be enabled in the content system picker. Default false. - - - principal - false - true - - Represents an optional attribute which provides Permission principal for CS integration. - - - - pickerMode - false - true - - Specific for CS browsing capabilities. Identifies what filtering (if any) - should be applied to the rendering of CS elements. - One of three possibilities. - FOLDER : CS picker should show CS folders only. - FILE : CS picker should show CS files only. - FILE_AND_FOLDER : CS pickers shows both files and folders. - - Default FILE. - - - - attachLabel - false - true - - Localized label for the "Attach File" element. Defaults to "Attach File" - - - - selectedLabel - false - true - - Localized label for the "Selected File" element. Defaults to "Selected File" - - - - currentLabel - false - true - - Localized label for the "Currently attached files" element. Defaults to "Currently attached files" - - - - doNotAttachLabel - false - true - - Localized label for the "Do not attach" element. Defaults to "Do not attach" - - - - bypassStepCheck - false - true - - Set to true if you really are in a step tag that is declared in another jsp where - the current jsp is included via jsp:include (so this taglib will not see the steptag - as a parent tag). If true then we will not fail the page rendering when a parent - StepTag is not found. - Defaults to "false" - - - - editableField - false - true - - Set to true if you really want text field should be editable field. - Defaults to "true" - - - - onAttachFile - false - true - - Javascript callback function triggered when a new file is attached. - Note: This event will only be triggered when allowMultipleFiles is true. - - -
    - - - filePickerListElement - - blackboard.servlet.tags.ngui.picker.FilePickerListElementTag - - empty - - Renders one of the default file picker list columns - - - type - true - true - - The type of column to display. Must be the string representation of one of the - enum constants in FilePickerListElementTag.Type. - - - - width - false - true - - Width of the column. - - - - align - false - true - - Alignment of the column. Should be one of: - - natural - - center - - inverse - - If not specified, will default to "natural" - - - - headerStyle - false - true - - Optional parameter indicating the CSS style to apply to the table header cells. - - Default: No style - - - - cellStyle - false - true - - Optional parameter indicating the CSS style to apply to the table data cells. - - Default: No style. - - - - title - false - true - Required for CUSTOM columns. Represents the column title. - - - id - false - true - Required for CUSTOM columns. Represents the column ID. - - - cellGeneratorFunction - false - true - - Required for CUSTOM columns. - - The fully qualified name of a javascript function that will be used to dynamically generate - the HTML for this cell (when a row is added in the browser). - - - - - - pickerListElement - - blackboard.servlet.tags.ngui.picker.PickerListElementTag - - JSP - - This tag both defines and renders a column in a picker list. - The body of the element will be evaluated for each item in the list. - - - id - true - true - - Unique id for the column - - - - title - false - true - - Localized title for the column. - - - - width - false - true - - Width of the column. - - - - align - false - true - - Alignment of the column. Should be one of: - - natural - - center - - inverse - - If not specified, will default to "natural" - - - - cellGeneratorFunction - false - true - - The fully qualified name of a javascript function that will be used to dynamically generate - the HTML for this cell (when a row is added in the browser). - - The arguments passed to this function vary depending on the type of picker. - - If this attribute is not specified, the "widget.PickerList.noopGenerator" function from widget.js - will be used (it returns an empty cell) - - - - headerStyle - false - true - - Optional parameter indicating the CSS style to apply to the table header cells. - - Default: 'smallCell' - - - - cellStyle - false - true - - Optional parameter indicating the CSS style to apply to the table data cells. - - Default: No style. - - - - - - mashupPicker - - blackboard.servlet.tags.ngui.picker.MashupPickerTag - - empty - - This tag renders a Flyout menu with a list of links to create available mashup items. - - - id - false - true - - Unique id for the picker menu. - - - - type - false - true - - Type of mashup items to display. Default: All Mashups. - - The valid values are: - * image - * video - * audio - * file - * all - - - - courseId - true - true - - Id of the current course. - - - - contentId - true - true - - Id of the content folder that the mashup item will be created in. - - - - - - - ifLicensed - blackboard.servlet.tags.IfLicensedTag - JSP - Include body if specified component is licensed. - - component - true - true - blackboard.platform.LicenseComponent - The component - - - - - - miniList - - blackboard.servlet.tags.ngui.list.MiniListTag - - blackboard.servlet.tags.ngui.picker.BaseListBasedPickerTei - JSP - - This tag displays a "mini-list" data list. Columns are defined - as child elements using the miniListElement tag - - - var - true - false - Variable to expose the current attached file to in the miniListElements. - - - items - true - true - List of items to loop through as rows of the list. - - - className - false - false - - Fully qualified class name of the type of objects in the items list. If not - specified, java.lang.Object will be used. - - - - description - false - true - - The list description that will be used for the summary of the table that - contains this mini-list. The description helps users browsing with accessibility - tools quickly understand what kind of information is in the list table. - The default summary for the table is: "This is a table showing the attributes of a collection of items." - - - - rowHeaderId - true - true - - miniListElement child tag that should be used as the row header - in this mini list table. The one chosen as the row header element should be the one containing - the most representative information of the object represented in the row. - For example, in a row with "Name", "Age", "Weight", and "Height", the id of the - miniListElement tag containing the "Name" should be the id to set here. - This attribute is for accessibility compliance, and must be set. - ]]> - - - - jsListVarName - false - true - - Name of a global javascript variable to expose the dynamic list object as. - - For example, if this variable is given the value "foo", you can add a row to the table - in the client-side javascript by calling foo.addRow(......); The values that are passed - to the addRow function depend on the parameters that your cell generator functions from the - miniListElement tags take. - - - - reorderable - false - true - - Whether this minilist is reorderable. Default: false - - - - reorderingUrl - false - true - - Url that will be posted to persist the drag and drop. If no immediate server-side changes - are necessary, this can be left blank and you can handle the reordering for example on page submit. - - For easy implementation, use a subclass of BaseListRepositioningAction for your server-side code. - - - - reorderType - false - true - A string describing the type of item being reordered (used as part of the title on the accessible repositioning controls). Required if reorderable. - - - itemIdAccessor - false - true - - Chained list of method names to get a String to be used as an - id for each table row. This attribute must be set for a - reorderable list. If more than one method need to be called in - sequence, "." should be used as a delimiter. The first method - in the attribute will be called against each object in the - collection. Then subsequent methods will be called against the - return value of the previous method call. All the methods - specified in the attribute must be accessible and the return - type of the final method must be String. No argument passing - and type casting will be done during methods invocation. For - example, if your collection object is - "blackboard.data.discussionboard.Conference" and the attribute - is "getId.getExternalString", - Conference.getId().getExternalString() will be invoked using - java reflection. - - - - itemNameAccessor - false - true - - Chained list of method names to get a String to be used as the - name of a reorderable item in accessible reordering controls. - This attribute must be set for a reorderable list. See the - description of reorderableItemIdAccessor attribute. - - - - contextParameters - false - true - - Query-string formatted string of additional request parameters to pass to the reordering struts - action. - - - - - - miniListElement - - blackboard.servlet.tags.ngui.picker.PickerListElementTag - - JSP - - This tag both defines and renders a column in a mini list. - The body of the element will be evaluated for each item in the list. - - - id - true - true - - Unique id for the column - - - - title - false - true - - Localized title for the column. - - - - width - false - true - - Width of the column. - - - - align - false - true - - -
  • natural
  • -
  • center
  • -
  • inverse
  • - - If not specified, will default to "natural". - ]]> -
    -
    - - cellGeneratorFunction - false - true - - The fully qualified name of a javascript function that will be used to dynamically generate - the HTML for this cell (when a row is added in the browser). - - The arguments passed to this function vary depending on the type of picker. - - If this attribute is not specified, the "widget.PickerList.noopGenerator" function from widget.js - will be used (it returns an empty cell) - - - - headerStyle - false - true - - Optional parameter indicating the CSS style to apply to the table header cells. - - Default: 'smallCell' - - - - cellStyle - false - true - - Optional parameter indicating the CSS style to apply to the table data cells. - - Default: No style. - - - - headerStickyNote - false - true - - Optional localized message to be shown associated with the entire column, as a means - to bring attention to the user, some peculiar information regarding the contents of - the column. - - -
    - - - - - contentList - - blackboard.servlet.tags.ngui.contentlist.ContentListTag - - JSP - - This is a scaffolding tag for the body of a Content List page. - The only children of this tag should be contentListItem tags. - - - designer - false - true - - Whether this content list should be rendered in design more (e.g. with drag and drop) - Default value: false. - - - - displayMode - false - true - - The mode that the list should be rendered in. Must be the string representation of one - of the ContentListTag.DisplayMode enum values. - Default value: iconAndText (DisplayMode.iconAndText) - - - - displayIndividualContentItem - false - false - - Default value: false, If set to true, render cotentItems in the contentlist tag individually - - - - contentItems - false - true - - A List of blackboard.data.content.Content that are the content items that should be - shown by the tag. This can be used instead of manually specifying content list item tags. - - - - contentIdsWithRules - false - true - - A List of Ids for the content items that have availability rules associated with them - - - - reorderingUrl - false - true - - Url that will be posted to to persist the drag and drop. - For easy implementation, use a subclass of BaseListRepositioningAction for your server-side code. - - - - reorderType - false - true - A string describing the type of item being reordered (used as part of the title on the accessible repositioning controls). Required if designer is true. - - - contextParameters - false - true - - Query-string formatted string of additional request parameters to pass to the struts - action. - - - - emptyMsg - false - true - - The localized text message to be displayed if the given collection is empty. - The default message is "No items found." - - - - type - false - true - - This value is used to specify that content list rendering should be done - for a specific type of object. This can be used to tailor behavior or - styling. See ContentListTag.Type for the list of available types. If - this value is not provided it defaults to ContentListTag.Type.Content. - - - - mayBeNested - false - true - - Default: true - If you know that this canvas is not going to contain any nested draggable areas - then you can set this parameter to false to speed up the drag/drop UI interaction - - - - - - contentListItem - - blackboard.servlet.tags.ngui.contentlist.ContentListItemTag - - JSP - - This tag represents an item in a content list. - It can optionally contain a delegateContextMenu tag which will render a - context menu next to the title of the item. The content of the item should be - specified in the body of the tag, and can contain any HTML/other jsp tags. - It can also optionally contain a contentListItemOppositeHtml tag to set - the opposite-html element - - - title - true - true - - Title of the content list item. - - - - titleColor - false - true - - Color of the title. - - - - titleSpanClass - false - true - - Wraps title with provided span class. - - - - url - false - true - - URL that the title should link to. If this attribute is not specified, - then no link will be added. - - - - iconUrl - false - true - - Optional URL to the icon that should be shown for this item. If no iconUrl is specified, - no icon will be shown for the item. - - - - id - false - true - - An id that uniquely identifies the item (e.g. the database id of the item ). - This will be used to identify the order of the items when reordering them. - - - - oppositeSideHtml - false - true - - A block of html to render on the right hand side of the title line for this item - - - - - - contentListItemOppositeHtml - - blackboard.servlet.tags.ngui.contentlist.ContentListItemOppositeHtmlTag - - JSP - - This tag represents the 'opposite-html' for an item in a content list. It will set - the opposite-html block for the parent contentMenuItem with the body of this tag. - - - - - learningUnitToc - - blackboard.servlet.tags.ngui.learningUnit.LearningUnitTocTag - - empty - - This tag displays the content of a learning unit (Lesson object) as a - hierarchical folder view with expand/collapse functionality - - - helper - true - true - - An instance of LearningUnitNavigationHelper that holds the data for the - learning unit to display. The current page should be set prior to be - passed to the tag. - - - - instructorView - false - true - - The learning unit TOC can operate in one of two modes: student view (the - default) and instructor view. These two views employ different rules for - what actually shows and how it shows. To enable instructor view this - property must be explicitly set to true. - - - - expandAll - false - true - - This value determines whether the tree nodes contained within the TOC - are all expanded on render. If true, then the full tree of items will - be expanded, if false, then items will be expanded or collapsed - according to information stored within the session (i.e., previously - set state for these nodes). The default value is false. - - - - location - false - true - - An instance of TocLocation. Determines the initial location in which - the TOC should render. If a value is not provided then a default value - of bottom will be used. - - - - size - false - true - - An instance of TocSize. Determines the initial size of the TOC. If a - value is not provided then a default value of normal (not minimized or - maximized) will be used. - - - - warning - false - true - - A warning message that should be displayed to the user in the event that - they click on one of the provided navigation controls within the table - of contents. This will take the form of a confirmation message - displayed to the user. - - - - - - learningUnitNavControls - - blackboard.servlet.tags.ngui.learningUnit.LearningUnitNavControlsTag - - empty - - This tag displays navigation controls for a learning unit (Lesson object). - This consists of previous/next buttons as well as page information. - - - helper - true - true - - An instance of LearningUnitNavigationHelper that holds the data for the - learning unit to display. The current page should be set prior to be - passed to the tag. - - - - warning - false - true - - A warning message that should be displayed to the user in the event that - they click on one of the provided navigation controls. This will take - the form of a confirmation message displayed to the user. - - - - - - searchResultList - - blackboard.servlet.tags.ngui.list.SearchResultListTag - - scriptless - - Generic search results list. It will loop over its body for each item in the items passed in. It will also - create paging controls based on the total results, current page, and results per page arguments. - - Note that the number of items in the {@code items} parameter does not have to have - any relation to the numbers used for constructing the paging information and vice - versa. All items in the {@code items} list are iterated over, even if there are more - than should logically be on the current page given the paging arguments. - - You can pass in one or more filters in the filters argument to generate filtering drop-downs for - the list. See the SearchResultFilter class for more information. - - - items - true - true - - List of items in the current page. The tag's body will be invoked once for each item in this list. - - - - var - true - true - - Variable name (put in the PageContext) to expose the current item as. - - - - filters - false - true - - A list of SearchResultFilter objects that define any filters to filter the search results. - See the SearchResultFilter class for more information. - - - - onFilterSubmit - false - true - - JavaScript to run when the "GO" button is clicked next to the filters. This will do nothing - if nothing is specified in the filters attribute. - - - - basePagingUrl - true - true - - Base URL to use when constructing the paging links. Two additional parameters: resultsPerPage, - and pageIndex will be appended to this URL. The pageIndex will be the index of the page to go - to. - - - - totalResults - true - true - - Total number of results in the search results. Used to construct paging widget. - - - - currentPage - true - true - - The current page (1-indexed.) Used to construct paging widget. - - - - resultsPerPage - true - true - - Number of results shown per page. Used to construct paging widget. - - - - allowEditPaging - false - true - - Flag to allow editing paging parameters. Default is false. - - - - allowShowAll - false - true - - Flag to allow the Show All button to appear. Default is false. - - - - cssClass - false - true - - An optional CSS class to apply to the button. - - - - - - - - inventoryList - - blackboard.servlet.tags.ngui.list.InventoryListTag - - - blackboard.servlet.tags.ngui.list.InventoryListTei - - JSP - - This is a scaffolding tag for an inventory list page and must - contain at least one list element. It creates an inventory - list used for displaying a collection of objects and its - attributes. Given a collection of objects, the paging - navigation will be automatically displayed. The initial number - of items per page is 25. This number is editable on the UI if - given a list options tag. In addition, there must be exactly - one list element tag in the body of this tag that has - isRowHeader=true. The list element most representative of the - rest of the attributes in the row should be designated as the - row header (For example, with Name, Age, Weight, and Height, - the Name should have isRowHeader=true). - - - collection - true - true - - The collection of objects passed to the list to be displayed. - - - - className - true - true - - The fully qualified class name of the objects in the collection. - - - - objectVar - true - true - - Variable name to represent a single object in the collection. - This is used in the list element tags to set the attribute of the - object to display. Coding naming standards should be followed here, - for example, do not have a space in your variable name. - - - - shoppingCart - false - true - - Indicates whether this list should behave as a shopping cart. In shopping - cart mode the check boxes are coupled with a page state 'the drawer'. When - an element is checked it is added to the drawer, and if removed from the - drawer it is also unchecked from the list. Drawer state survives page refresh - and, once the list is displayed, its check boxes are updated to match the current - state of the drawer. - - - - searchCriteria - false - true - - Search criteria used to populate the list and render the active filter UI component. - When the active filter is modified, it will send an ajax request to the server with - the modified search criteria. The server renders and returns a new page using the - modified search criteria via ajax. The active filter and list ui components are then - extracted and rendered in the designated div tag. Active filter state is preserved. - This same process is invoked for these additional page actions: paging (prev/next), - refresh, and list sorting.Hence, if searchCriteria is specified it will always render - the inventory list in ajax mode. All page refreshes via the active filter, sorting - list columns, paging list results will happen via ajax post. - - - - emptyMsg - false - true - - The localized text message to be displayed if the given collection is empty. - The default message is "No items found." - - - - emptyMsgCustomClass - false - true - - If desired, an additional css class can be specified here to use in addition to 'noItems' - when rendering the emptyMsg div. - - - - description - false - true - - The list description that will be used for the summary of the table that - contains this list. The description helps users browsing with accessibility - tools quickly understand what kind of information is in the list table. - The default summary for the table is: "This is a table showing the - attributes of a collection of items." - - - - showDescriptionAsHeader - false - true - - Default Value: false. - If set to true then the list description will also be shown as a visible header before the table. - Note: If the list is empty this title will not be shown, regardless of this value - - - - initialSortCol - false - true - - The column to initially sort by when the list is displayed. - The value should match the name of a list element. - - - - initialSortBy - false - true - - The initial direction of sort of the initial sort column. - Accepted values are ASCENDING and DESCENDING. - The default is sort by ascending. - - - - url - false - true - - Url of current page to be used in inventory list links like sort and paging. - - - - includePageParameters - false - true - - Flag denoting whether to include page parameters in the list urls. When set to false, - only list tag related params will be appended to the provided url or the one obtained - from request. - Default value is true, therefore unless some hidden input should be carried - forward to next pages, such as search parameters, it is recommended to consider - setting this value to false. Also, if a page has input that is too long and should - not be appended to url, such as textbox or formatted text description, or can be - reached from another action as forward, this attribute may need to be set to false. - - - - reorderable - false - true - - Whether this inventory list is reorderable. Default: false - - - - reorderingUrl - false - true - - Url that will be posted to persist the drag and drop. - For easy implementation, use a subclass of BaseListRepositioningAction for your server-side code. - - - - reorderType - false - true - A string describing the type of item being reordered (used as part of the title on the accessible repositioning controls). Required if reorderable. - - - itemIdAccessor - false - true - - Chained list of method names to get a String to be used as an - id for each table row. This attribute must be set for a - reorderable list. If more than one method need to be called in - sequence, "." should be used as a delimiter. The first method - in the attribute will be called against each object in the - collection. Then subsequent methods will be called against the - return value of the previous method call. All the methods - specified in the attribute must be accessible and the return - type of the final method must be String. No argument passing - and type casting will be done during methods invocation. For - example, if your collection object is - "blackboard.data.discussionboard.Conference" and the attribute - is "getId.getExternalString", - Conference.getId().getExternalString() will be invoked using - java reflection. - - - - itemNameAccessor - false - true - - Chained list of method names to get a String to be used as the - name of a reorderable item in accessible reordering controls. - This attribute must be set for a reorderable list. See the - description of reorderableItemIdAccessor attribute. - - - - contextParameters - false - true - - Query-string formatted string of additional request parameters to pass to the struts - action. - - - - showAll - false - true - - Boolean attribute for displaying the all items in the list. The default is false for - showing all items. - - - - renderAjax - false - false - - - - - - listId - false - true - - - - - - displayPagingControls - false - true - - Default: true, but if set to false then the paging controls will not be displayed - - - - pagingInfo - false - true - - Provides showAll/pageIndex/pageSize/sortCol/sortDir/recordCount informations - about a data-set, then in the TagHelper we needn't get again. This is - used for paging. - - - - - - pagedList - - blackboard.servlet.tags.ngui.list.PaginationListTag - - - blackboard.servlet.tags.ngui.list.InventoryListTei - - JSP - - This is a scaffolding tag for an inventory list page and must - contain at least one list element. It creates an inventory - list used for displaying a collection of objects and its - attributes. Given a collection of objects, the paging - navigation will be automatically displayed. The initial number - of items per page is 25. This number is editable on the UI if - given a list options tag. In addition, there must be exactly - one list element tag in the body of this tag that has - isRowHeader=true. The list element most representative of the - rest of the attributes in the row should be designated as the - row header (For example, with Name, Age, Weight, and Height, - the Name should have isRowHeader=true). - - - collection - true - true - - The collection of objects passed to the list to be displayed. - - - - className - true - true - - The fully qualified class name of the objects in the collection. - - - - objectVar - true - true - - Variable name to represent a single object in the collection. - This is used in the list element tags to set the attribute of the - object to display. Coding naming standards should be followed here, - for example, do not have a space in your variable name. - - - - emptyMsg - false - true - - The localized text message to be displayed if the given collection is empty. - The default message is "No items found." - - - - description - false - true - - The list description that will be used for the summary of the table that - contains this list. The description helps users browsing with accessibility - tools quickly understand what kind of information is in the list table. - The default summary for the table is: "This is a table showing the - attributes of a collection of items." - - - - initialSortCol - false - true - - The column to initially sort by when the list is displayed. - The value should match the name of a list element. - - - - initialSortBy - false - true - - The initial direction of sort of the initial sort column. - Accepted values are ASCENDING and DESCENDING. - The default is sort by ascending. - - - - url - false - true - - Url of current page to be used in inventory list links like sort and paging. - - - - reorderable - false - true - - Whether this inventory list is reorderable. Default: false - - - - reorderingUrl - false - true - - Url that will be posted to persist the drag and drop. - For easy implementation, use a subclass of BaseListRepositioningAction for your server-side code. - - - - reorderType - false - true - A string describing the type of item being reordered (used as part of the title on the accessible repositioning controls). Required if reorderable. - - - itemIdAccessor - false - true - - Chained list of method names to get a String to be used as an - id for each table row. This attribute must be set for a - reorderable list. If more than one method need to be called in - sequence, "." should be used as a delimiter. The first method - in the attribute will be called against each object in the - collection. Then subsequent methods will be called against the - return value of the previous method call. All the methods - specified in the attribute must be accessible and the return - type of the final method must be String. No argument passing - and type casting will be done during methods invocation. For - example, if your collection object is - "blackboard.data.discussionboard.Conference" and the attribute - is "getId.getExternalString", - Conference.getId().getExternalString() will be invoked using - java reflection. - - - - itemNameAccessor - false - true - - Chained list of method names to get a String to be used as the - name of a reorderable item in accessible reordering controls. - This attribute must be set for a reorderable list. See the - description of reorderableItemIdAccessor attribute. - - - - contextParameters - false - true - - Query-string formatted string of additional request parameters to pass to the struts - action. - - - - recordCount - true - true - - Provides actual size of live data-set. - - - - displayPagingControls - false - true - - Default: true, but if set to false then the paging controls will not be displayed - - - - - - settingsPage - - blackboard.servlet.tags.ngui.datacollection.SettingsPageTag - - JSP - Scaffolding for a settings page. Similar to a data collection page but - steps are not required/permitted while a stepSubmit is required. - - - - - settingsPageList - - blackboard.servlet.tags.ngui.list.SettingsPageListTag - - - blackboard.servlet.tags.ngui.list.InventoryListTei - - JSP - - This is a scaffolding tag for a settings page list. Similar to an - inventory list except: - a) No paging (all items will be displayed in one page) - b) Different styles - - - collection - true - true - - The collection of objects passed to the list to be displayed. - - - - className - true - true - - The fully qualified class name of the objects in the collection. - - - - objectVar - true - true - - Variable name to represent a single object in the collection. - This is used in the list element tags to set the attribute of the - object to display. Coding naming standards should be followed here, - for example, do not have a space in your variable name. - - - - emptyMsg - false - true - - The localized text message to be displayed if the given collection is empty. - The default message is "No items found." - - - - description - false - true - - The list description that will be used for the summary of the table that - contains this list. The description helps users browsing with accessibility - tools quickly understand what kind of information is in the list table. - The default summary for the table is: "This is a table showing the - attributes of a collection of items." - - - - initialSortCol - false - true - - The column to initially sort by when the list is displayed. - The value should match the name of a list element. - - - - initialSortBy - false - true - - The initial direction of sort of the initial sort column. - Accepted values are ASCENDING and DESCENDING. - The default is sort by ascending. - - - - url - false - true - - Url of current page to be used in inventory list links like sort and paging. - - - - reorderable - false - true - - Whether this inventory list is reorderable. Default: false - - - - reorderingUrl - false - true - - Url that will be posted to persist the drag and drop. - For easy implementation, use a subclass of BaseListRepositioningAction for your server-side code. - - - - reorderType - false - true - A string describing the type of item being reordered (used as part of the title on the accessible repositioning controls). Required if reorderable. - - - itemIdAccessor - false - true - - Chained list of method names to get a String to be used as an - id for each table row. This attribute must be set for a - reorderable list. If more than one method need to be called in - sequence, "." should be used as a delimiter. The first method - in the attribute will be called against each object in the - collection. Then subsequent methods will be called against the - return value of the previous method call. All the methods - specified in the attribute must be accessible and the return - type of the final method must be String. No argument passing - and type casting will be done during methods invocation. For - example, if your collection object is - "blackboard.data.discussionboard.Conference" and the attribute - is "getId.getExternalString", - Conference.getId().getExternalString() will be invoked using - java reflection. - - - - itemNameAccessor - false - true - - Chained list of method names to get a String to be used as the - name of a reorderable item in accessible reordering controls. - This attribute must be set for a reorderable list. See the - description of reorderableItemIdAccessor attribute. - - - - contextParameters - false - true - - Query-string formatted string of additional request parameters to pass to the struts - action. - - - - - - listOptions - - blackboard.servlet.tags.ngui.list.ListOptionsTag - - empty - - Tag that creates paging options for the current inventory - list. Paging options include showing refresh, "Show All", and - "Edit Paging" buttons and configuration attributes for those - options. This tag can only be used inside an inventory list - tag. - - - allowRefresh - false - true - - Boolean attribute for showing a refresh button or not. The - refresh button would refresh the contents of the list. The - default is false for not showing a refresh button. - - - - allowShowAll - false - true - - Boolean attribute for displaying the Show All button and - allowing the show all action. The default is true for - showing a Show All button. - - - - allowEditPaging - false - true - - Boolean attribute for displaying the Edit Paging button - and allowing users to enter how many items the want to see - on the page at a time. The default is true for showing and - allowing Edit Paging. - - - - showAllMax - false - true - - The maximum number of items to show on the page when the - user chooses to show all, only effective when allowShowAll - is true. This value may be set for performance reasons, - limiting the size of the load to the page. The default is - no maximum. - - - - pagingMax - false - true - - The maximum number of items to show on one page, only - effective when allowEditPaging is true. The default is no - maximum. - - - - refreshUrl - false - true - - The url for the refresh button. This must be specified, - and is only effective, when allowRefresh is true. - - - - displayCountAtTop - false - true - - Default: false, but if set to true then the total count of records is - displayed at the top as well. - - - - - - listOptionElementHeader - - blackboard.servlet.tags.ngui.list.ListOptionElementHeaderTag - - scriptless - - - - - selectAll - false - true - - Boolean attribute to show or hide the select all list option elements - - - - clearAll - false - true - - Boolean attribute to show or hide the clear all list option elements - - - - legend - false - true - - String attribute specifying the fieldset legend wrapping the set of options - - - - - - - listFilterSelectionType - - blackboard.servlet.tags.ngui.list.ListFilterSelectionTypeTag - - scriptless - - - - - id - false - true - - unique id of the select html element. If not provided will - be name_id, appended with top/bot suffix value - - - - name - true - true - - unique name of the select html element. This is mandatory field to be given. - - - - defaultLabel - false - true - - The default label to begin with in the option group for the select element. If not given, the - top element in the nameValue list will default to the first in the option group. - - - - onBlur - false - true - - call back js method for onblur event on the select html element - - - - onChange - false - true - - call back js method for onchange event on the select html element - - - - labelValue - false - true - - The list of ItemDetail objects with name and values to be dispplayed as the option group - for the select element - - - - - - listActionBar - - blackboard.servlet.tags.ngui.list.ListActionBarTag - - scriptless - - - Action button items and menus may be added to the action bar. - Buttons are added via the listActionItem tag. Action - menus are added via the listActionMenu tag. - The list action bar will not be shown if there is no items - in the list with a checkbox. -

    - ]]> -
    -
    - - - listActionMenu - - blackboard.servlet.tags.ngui.list.ListActionMenuTag - - scriptless - - items attribute or - listActionItem tags in the body. - ]]> - - - title - true - true - - List action menu title. The menu title should be localized text. - - - - items - false - true - - List of list action items (blackboard.servlet.data.ngui.ListActionItem) with which to - populate the list action menu. - - - - - - listActionItem - - blackboard.servlet.tags.ngui.list.ListActionItemTag - - scriptless - - listActionBar tag, which - will generate buttons, or within the listActionMenu tag, which will - generate items inside the action menu. - - If body content is specified and the item is not in a listActionMenu, - the contents will be displayed before the action item link. - ]]> - - - title - true - true - - Action item title. The title should be localized text. - - - - id - false - true - - id of list item - - - - url - true - true - The URL where the item points. - - - onClick - false - true - The onClick javascript handler for the link. - - - minSelected - false - true - - The minimum number of rows selected to perform the action. - The default minimum number of selected rows is 1. - - - - contextMenuItemId - false - true - - listContextMenu tag). Default is empty string. -

    - Specify the context menu item id here, and refer to this id in the context menu - ordering string for this list action item to show in the context menu. See - the list context menu tag description for more details about specifying ordering. -

    - Note: List action items will not show in context menu even if referenced - in the order string if the current row does not show a checkbox/radio - or if minSelected attribute is greater than 1, - meaning the action is meant to be executed on multiple items only. - Remember, list checkbox columns should be added via the - listCheckboxElement tag and list radio button columns should - be added via the listRadioElement tag. -

    - Remember, list checkbox columns should be added via the listCheckboxElement tag, list radio - buttons via the listRadioElement tag, and attribute list checkbox/radio column via the - attrListSelectColum tag. - ]]> - - - - cssClass - false - true - - The list item css class to apply if the action menu is associated with other body elements - - - - onRefreshJsSnippet - false - true - - @Since 9.1, set this snippet if this js needs to be executed on refresh of the parent list tag - - - - titleIsDisplayOnly - false - false - - @Since 9.1 - Defaults to false. - If true, the title is shown before the contents of the tag and is not presented as an - action button - It is merely displayed on the page as descriptive text. - This is only valid when present within a listActionBar - ignored inside a listActionMenu. - ALSO, in this mode the listactionitem + body contents are only displayed at the TOP of the list, not in - the bottom action bar. - - - - - - listElement - blackboard.servlet.tags.ngui.list.ListElementTag - JSP - - A list element represents a column in the inventory list. Columns are sortable only when the - comparator attribute is set. The contents of this tag will be the value of an attribute of a - single object (a single "cell"). The collection of the bodies of list elements make up a single - "object row" in the list table (for example, First Name, Last Name, Email). - - - name - true - true - - Name of this list element used by the parent inventory list tag when - determining what the initial sort column is, for example. - - - - label - false - true - The label for this column. The label should be localized text. - - - accessibilityLabelOnly - false - true - - @Since 9.1 - By default this is false. If set to true, the label on the column will NOT be shown. - Instead, it will be placed in a hidden span so that only screen readers will use it. - The column will also be formatted with the same smaller-cell style used for checkbox/radio button - columns since the intent of this would be for an icon-only row-actions column. - - - - comparator - false - true - - The comparator used to sort this column. The column is considered - sortable when this attribute is set. - - - - nowrap - false - true - - Sets the nowrap HTML attribute for the column. - The default is true to allow wrapping. - - - - isRowHeader - false - true - - Boolean attribute indicating that the information in the body of this tag is the most - representative of the object represented in the row (for example, with Name, Age, Weight, and Height, - the Name should be the one with isRowHeader=true). This setting is used to set row header tags as well - as title attributes to check boxes (if any) within the list table for accessibility compliance. The - default value is false. - - - - tdClass - false - true - The css class to be applied to the td cell (Default: ""). - - - displayElement - false - true - - This attribute will allow the list table to have the column but not display (Style="display:none"). If this is set - to false, the element will be added to the list container table but will be prevented from being displayed to the user. - This is useful in cases when we want to display different information inside of the drawer, since the drawer pull up - HTML from the inventory list it is bound to. By default this is set to true; - - - - - - listCheckboxElement - blackboard.servlet.tags.ngui.list.ListCheckboxElementTag - empty - - A list element representing a column of checkboxes in the list (for e.g. inventoryList,contentList e.t.c). - The title of the checkboxes, for accessibility compliance, will automatically be the value of the list element set as - the row header since the column specified will be the most representative information out of all - the other information in the same row. - NOTE: You may not conditionally use this tag per row. If present on the jsp then you - must include this on every row. To hide the checkbox use showCheckbox="false" instead of skipping - the tag. - - - id - false - true - - The id of this form element. If this listCheckbox option element is used with any list other than - the inventory list and its children, it is mandatory to provide this value. It should match the id of the list item - this list option element is associated with - - - - name - true - true - - The name of this form element. This corresponds to the name - attribute of the HTML input tag. - - - - value - true - true - - The value of this element. This corresponds to the value - attribute of the HTML input tag. - - - - showCheckbox - false - true - - Boolean attribute indicating whether the checkbox should be shown or not. - The default is true. - - - - title - false - true - - The title for this form element. This corresponds to the title - attribute of the HTML input tag. - - - - label - false - true - - The localized text label of the checkbox. - - - - className - false - true - - The class value to be applied to this input element - - - - - - listRadioElement - blackboard.servlet.tags.ngui.list.ListRadioElementTag - empty - - A list element representing a column of checkboxes in the list (for e.g. inventoryList,contentList e.t.c). - The title of the radio button, for accessibility compliance, will automatically be the value of the list element set as - the row header since the column specified will be the most representative information out of all - the other information in the same row. - - - name - true - true - - The name of this form element. This corresponds to the name - attribute of the HTML input tag. - - - - id - false - true - - The id of this form element. If this listRadio option element is used with any list other than - the inventory list and its children, it is mandatory to provide this value. It should match the id of the list item - this list option element is associated with - - - - value - true - true - - The value of this element. This corresponds to the value - attribute of the HTML input tag. - - - - showRadio - false - true - - Boolean attribute indicating whether the radio button should be shown or not. - The default is true. - - - - title - false - true - - The title for this form element. This corresponds to the title - attribute of the HTML input tag. - - - - label - false - true - - The localized text label of the checkbox. - - - - - - listContextMenu - blackboard.servlet.tags.ngui.list.ListContextMenuTag - scriptless - - listElement tag. -

    - Ordering context menu items -

    - There are 2 ways to give ordering to list context menu items: -

      -
    1. - Give the order key from the context menu properties file - to the orderKey attribute. -
    2. -
    3. Give the order string to the order attribute.
    4. -
    -

    - There are currently 3 ways context menu items are added to the page. - Here is how to get the order string for each type of item: -

      -
    1. - Dynamically generated nav items for context menu items - - order is referenced by the internal handle of the nav item -
    2. -
    3. - Manually specified context menu items in jsp via contextMenuItem tags - - provide the id attribute for contextMenuItem tag OR - reference the default id string assigned to each context menu item (see TLD for - contextMenuItem tag for more information) -
    4. -
    5. - List action bar action items - - provide the contextMenuItemId attribute for the - listActionItem tag -
    6. -
    -

    - Additional notes -

    -

      -
    • - Ids, if provided, for context menu items must be unique within each context menu. -
    • -
    • - List action items will not show in context menu even if referenced - in the order string if the current row does not show a checkbox/radio or - if minSelected attribute to list action item is greater than 1, - meaning the action is meant to be executed on multiple items only. -
    • -
    • - If orderKey does not exist in the properties file, an error - will be logged to the default log file. -
    • -
    • - The context menu must have at least 1 item in it if not dynamically generated. -
    • -
    -

    - Example usage of context menu item ordering: -

    - - <bbNG:inventory list ...>
    -   <bbNG:listActionBar>
    -     <bbNG:listActionItem title="Delete Me" url="${actionItemUrl}" contextMenuItemId="delete_list_action"/>
    -     ...
    -   </bbNG:listActionBar>
    -
    -   <bbNG:listElement ...>
    -     ...
    -     <bbData:constants type="blackboard.servlet.tags.ngui.ContextMenuTag"/>
    -     <bbNG:listContextMenu dynamic="true" - menuGeneratorURL="/webapps/blackboard/execute/modifyUserNavItemBasedContextMenuGenerator" - navItem="list_users" - contextParameters="xx" - order="user_properties,password_modify,${ContextMenuTag['SEPARATOR']},list_courses_by_user,manual_item1, - ${ContextMenuTag['DEFAULT_ITEM_ID_PREFIX']}1,${ContextMenuTag['SEPARATOR']},delete_list_action">
    -       <bbNG:contextMenuItem title="Manual item1" url="xx" id="manual_item1"/>
    -       <bbNG:contextMenuItem title="Manual item2" url="xx"/>
    -     </bbNG:listContextMenu>
    -
    -   </bbNG:listElement>
    -   ...
    -
    - </bbNG:inventoryList> -
    -

    - The code will generate: -

    -

    -
    - User Properties
    - Change Password
    -
    -
    - Course Enrollments
    - Manual item1
    - Manual item2
    -
    -
    - Delete Me
    -
    -
    - [all other manual context menu items not referenced in order string]
    - [all other dynamically loaded context menu items not referenced in order string]
    -
    -
    - ]]> -
    - - menuLinkTitle - false - true - Text for the title attribute of the context menu link. If not specified will default to "Menu Options: [the name of container list element tag]" - - - navItem - false - true - - Handle of the root navigation item to grab the context menu items from. - All children nav items of the specified item will be used to populate the context menu. - - - - dynamic - false - true - Whether this context menu is populated at run time. - - - menuGeneratorURL - false - true - Url that the dynamic options will be loaded from. - - - contextParameters - false - true - Context parameters needed for resolution of href's. - - - orderKey - false - true - - base/config/internal/contextMenuOrder.properties - file to load the context menu item order from. - ]]> - - - - order - false - true - - ContextMenuTag class for the separator and default item id strings. -

    - Example: -

    - <bbData:constants type="blackboard.servlet.tags.ngui.ContextMenuTag"/> -
    - <bbNG:listContextMenu order="sample_item1,${ContextMenuTag['SEPARATOR']},${ContextMenuTag['DEFAULT_ITEM_ID_PREFIX']}"...> - ]]> - - - - renderInline - false - true - - - - - - - - listElementAvailabilitySubheading - blackboard.servlet.tags.ngui.list.ListElementAvailabilitySubheadingTag - empty - - Simple container tag for display availability information in the subheading of the listElement tag. - This should only be used inside a listElement tag, and is generally intended for use in the - first column of the list, to indicate to an instructor that the list row is not visible to students. - - - availability - false - true - - Display string that describes the availability of the list element - - - - - - genericFieldComparator - blackboard.servlet.tags.ngui.list.GenericFieldListComparatorTag - empty - - Simple tag that instatiates a GenericFieldComparator for use by - ListElementTag. - - - className - false - true - - Fully qualified class name of the class of the object being compared. - If this is omitted, the tag will look up at the InventoryListTag for the className. - - - - accessorMethod - true - true - - Name of the accessort method that will be used to get the - attribute used to compare objects. - - - - ascending - false - true - Whether default sort order should be ascending (Default true). - - - - - beanComparator - blackboard.servlet.tags.ngui.list.BeanListComparatorTag - empty - - Simple tag that instatiates a commons-beanutils BeanComparator for use by - ListElementTag. - - - property - true - true - Name of the bean property that will be used to compare objects. - - - ascending - false - true - Whether default sort order should be ascending (Default true). - - - - - - - attrList - blackboard.servlet.tags.AttributeListTag - blackboard.servlet.tags.AttributeListTei - JSP - - Displays an attribute list. This list depends on a family of subsidiary - tags to set up its control columns, renders, and column properties. - - - listId - true - true - blackboard.platform.attributelist.ListId - - - - - model - true - true - blackboard.platform.attributelist.AttributeListModel - - Contains the data to display in the list - - - - sortUrl - false - true - - The URL to sort or paginate this list, probably of the form - "myPage.jsp?id=_1_2&name=test" or to control the list display, also specify - "myPage.jsp?id=_1_2&name=test&sortBy=" + listElement_index + - "&startIndex=" + ind - - - - itemLabel - false - true - A label for the objects in the list, default is "Objects" - - - encodeParameters - false - true - - Determines whether the parameters on the URL string of the paging links should be - URL encoded. Default is true, and normally won't need to be changed - - - - startPageIndex - false - true - - The page index as an integer that the list widget should display (optional). The - list tag first looks for a request parameter for the page index value. If it - doesn't find one, it uses this value. If not specified, the first page will be - displayed - - - - description - false - true - - A description of the purpose of the list. This will be placed in the underlying - table's "summary" field, where it can be digested by screen readers. The - provided text should be localized. If no description is provided, the table will - generate a default summary based on the given itemLabel attribute. - - - - rowsPerPage - false - true - - The number of items to display on a given page. Default is 25, and -1 is - "show all" - - - - rowObj - false - true - java.lang.String - - The name of the attribute to place the current row object into, which is - of type AttributeListItem. This attribute will be made available to all - subsidiary tags. - - - - colObj - false - true - java.lang.String - - The name of the attribute to place the current column object into, which is - of type AttributeColumnDefinition. This attribute will be made available to - all subsidiary tags. - - - - cellObj - false - true - java.lang.String - - The name of the attribute to place the current cell object into. This attribute - will be made available to all subsidiary tags. - - - - halign - false - true - java.lang.String - - The horizontal alignment to use for all cells in the table. This can be - overriden at the column level. Valid values are 'left', 'right', and - 'center'. - - - - valign - false - true - java.lang.String - - The vertical alignment to use for all cells in the table. This can be - overriden at the column level. Valid values are 'top', 'bottom', and - 'middle'. - - - - width - false - true - java.lang.String - - The width of the table, in either pixel or percentage terms. Default - value is 100#. - - - - displayCustomizeButton - false - true - - Whether the list should display the customization button. If a <titleBar> - tag is available on the same page, the list will attempt to place the button - there. If not, it will place it above the list. - - - - displayBreadcrumbBar - false - true - - Whether the customization page reached via the customize button should - display a breadcrumb bar. - - - - href - false - true - - A URL that identifies the page on which the list is placed, and will - load the page as it's currently displayed if invoked. This attribute is - only necessary if the list is configured to display a customize button, - and does not exist in a navigation item context. - - - - target - false - true - - The href target of the page. This attribute is only necessary if this - if the list is configured to display a customize button, and does not - exist in a navigation item context. - - - - title - false - true - - The title of this page, as it should appear in a breadcrumb bar. - This attribute is only necessary if if the list is configured to display a - customize button, and does not exist in a navigation item context. - - - - noResultsMessage - false - true - - The message to display if the model contains no results. If not specified, no - message will be displayed - - - - alwaysDisplayHeaders - false - true - - Whether to display column headers when the list has no content. - - - - allowEditPaging - false - true - - Whether the items per page can be modified by the user. Default is true. - - - - allowRefresh - false - true - - Whether the refresh button should appear. Default is false. - - If this is specified, the refreshUrl attribute must also be set. - - - - refreshUrl - false - true - - A URL that identifies the location that should be used for the refresh button. - - - - allowShowAll - false - true - - true/false. If true, a show all button is displayed with the list. Defaults to false. - - - - - - - attrListContextMenu - blackboard.servlet.tags.AttributeListContextMenuTag - scriptless - - attrList tag. - The attrList tag acts as a renderer delegate and will put the context menu in the first - non user-removable (permanent) column it detects from its model, including columns manually - specified within the body of the attrList tag. -

    - The context menu provides a set of actions to take on the item. - By default, attribute inventory list actions that can apply to a just single item (minSelected attribute of the - list action item tag) will appear in the context menu. The rest of the contents - in the context menu can be populated either: directly from a nav item, explicitly using - contextMenuItem tags, or dynamically. The context menu must have at least 1 item in it. -

    - Ordering context menu items -

    - There are 2 ways to give ordering to list context menu items: -

      -
    1. - Give the order key from the context menu properties file - to the orderKey attribute. -
    2. -
    3. Give the order string to the order attribute.
    4. -
    -

    - There are currently 3 ways context menu items are added to the page. - Here is how to get the order string for each type of item: -

      -
    1. - Dynamically generated nav items for context menu items - - order is referenced by the internal handle of the nav item -
    2. -
    3. - Manually specified context menu items in jsp via contextMenuItem tags - - provide the id attribute for contextMenuItem tag OR - reference the default id string assigned to each context menu item (see TLD for - contextMenuItem tag for more information) -
    4. -
    5. - List action bar action items - - provide the contextMenuItemId attribute for the - listActionItem tag -
    6. -
    -

    - Additional notes -

    -

      -
    • - Ids, if provided, for context menu items must be unique within each context menu. -
    • -
    • - List action items will not show in context menu even if referenced - in the order string if the current row does not show a checkbox/radio or - if minSelected attribute to list action item is greater than 1, - meaning the action is meant to be executed on multiple items only. -
    • -
    • - If orderKey does not exist in the properties file, an error - will be logged to the default log file. -
    • -
    • - The context menu must have at least 1 item in it if not dynamically generated. -
    • -
    -

    - Example usage of context menu item ordering: -

    - - <bbNG:attrList ...>
    -   <bbNG:listActionBar>
    -     <bbNG:listActionItem title="Delete Me" url="${actionItemUrl}" contextMenuItemId="delete_list_action"/>
    -     ...
    -   </bbNG:listActionBar>
    -
    -   <bbNG:attrListColumn ...>
    -     ...
    -
    -   </bbNG:attrListColumn>
    -   ...
    - ...
    -  <bbData:constants type="blackboard.servlet.tags.ngui.ContextMenuTag"/>
    -  <bbNG:attrListContextMenu dynamic="true" - menuGeneratorURL="/webapps/blackboard/execute/modifyUserNavItemBasedContextMenuGenerator" - navItem="list_users" - contextParameters="xx" - order="user_properties,password_modify,${ContextMenuTag['SEPARATOR']},list_courses_by_user,manual_item1, - ${ContextMenuTag['DEFAULT_ITEM_ID_PREFIX']}1,${ContextMenuTag['SEPARATOR']},delete_list_action">
    -  <bbNG:contextMenuItem title="Manual item1" url="xx" id="manual_item1"/>
    -   <bbNG:contextMenuItem title="Manual item2" url="xx"/>
    -  </bbNG:attrListContextMenu>
    - </bbNG:attrList> -
    -

    - The code will generate: -

    -

    -
    - User Properties
    - Change Password
    -
    -
    - Course Enrollments
    - Manual item1
    - Manual item2
    -
    -
    - Delete Me
    -
    -
    - [all other manual context menu items not referenced in order string]
    - [all other dynamically loaded context menu items not referenced in order string]
    -
    -
    - - - ]]> -
    - - menuLinkTitle - false - true - Text for the title attribute of the context menu link. If not specified will default to: Click to see options - - - navItem - false - true - - Handle of the root navigation item to grab the context menu items from. - All children nav items of the specified item will be used to populate the context menu. - - - - dynamic - false - true - Whether this context menu is populated at run time. - - - menuGeneratorURL - false - true - Url that the dynamic options will be loaded from. - - - contextParameters - false - true - Context parameters needed for resolution of href's. - - - orderKey - false - true - - base/config/internal/contextMenuOrder.properties - file to load the context menu item order from. - ]]> - - - - order - false - true - - ContextMenuTag class for the separator and default item id strings. -

    - Example: -

    - <bbData:constants type="blackboard.servlet.tags.ngui.ContextMenuTag"/> -
    - <bbNG:attrListContextMenu order="sample_item1,${ContextMenuTag['SEPARATOR']},${ContextMenuTag['DEFAULT_ITEM_ID_PREFIX']}"...> - ]]> - - - - - - attrListColumn - blackboard.servlet.tags.AttributeListColumnTag - JSP - - Define display attributes for one or more columns, including column - renderers. There are three ways to define a renderer: via a class - name representing a renderer class, via a renderer object (placed - in a request attribute), or via template text placed directly in the - body of the tag. - - - cols - true - true - java.lang.String - - The names of the columns to which this render should be applied. Multiple - columns should be separated by commas. - - - - rendererClassName - false - true - java.lang.String - - The name of the renderer class. Must implement the AttributeListCellRenderer - interface. - - - - renderer - false - true - blackboard.servlet.attributelist.AttributeListCellRenderer - - The name of an attribute containing a renderer object to use for - all cells in this column. Object must implement the - AttributeListCellRenderer interface. - - - - halign - false - true - java.lang.String - - The horizontal alignment of data in the list's cells. Valid values - are 'left', 'right', and 'center'. If no default has been defined - by the parent table tag, then default is 'center', - - - - valign - false - true - java.lang.String - - The horizontal alignment of data in the list's cells. Valid values - are 'top', 'bottom', and 'middle'. If no default has been defined - by the parent table tag, then default is 'middle'. - - - - width - false - true - java.lang.String - - The width of the column, in either pixel or percentage terms - - - - weight - false - true - - The weight of the column. Columns are sized based on their relative weights. - - - - nowrap - false - true - - Sets the nowrap html attribute for the column. Default is to allow wrapping - - - - comparatorProperty - false - true - java.lang.String - - The property that this column represents, for the purposes of - row comparison when performing sorts. - - - - comparator - false - true - java.util.Comparator - - A comparator to use when comparing one row to the other, for - list sorting. The comparator will receive AttributeListItem - objects, and so must know how to deal with them. - - - - isRowHeader - false - true - - Whether this column's rows are a row header. Default false. - - - - - - attrListSelectColumn - blackboard.servlet.tags.AttributeListSelectColumnTag - JSP - - Displays the column of checkboxes or radio buttons in the attribute list - - - type - true - true - java.lang.String - - The type of item. Must be either "checkbox" or "radio" - - - - showItem - false - true - java.lang.String - - Whether to show the item. Default true. - - - - value - true - true - java.lang.String - - The value to assign to the checkbox/radio element. The application may use - the row, column, and cell objects exposed by the list in generating - this value. - - - - name - true - true - java.lang.String - - The name to give to the checkbox/radio element. The application may use - the row, column, and cell objects exposed by the list in generating - this value. - - - - halign - false - true - java.lang.String - - The horizontal alignment of data in the list's cells. Valid values - are 'left', 'right', and 'center'. If no default has been defined - by the parent table tag, then default is 'center'. - - - - valign - false - true - java.lang.String - - The horizontal alignment of data in the list's cells. Valid values - are 'top', 'bottom', and 'middle'. If no default has been defined - by the parent table tag, then default is 'middle'. - - - - width - false - true - java.lang.String - - The width of the column, in either pixel or percentage terms - - - - isRowHeader - false - true - - Whether this column's rows are a row header. Default false. - - - - - - attrListControlColumn - blackboard.servlet.tags.AttributeListControlColumnTag - JSP - - Displays a generic control column in the attribute list. Cell content - is defined via template text provided in the body of this tag. - - - lockLocation - true - true - - Whether to lock the control column to the left of right of the list. - Valid values are 'start' and 'end'. - - - - position - true - true - - The position of the column within its lock location. - - - - header - false - true - - The text that should appear in the column header. - Defaults to an empty string. - - - - halign - false - true - java.lang.String - - The horizontal alignment of data in the list's cells. Valid values - are 'left', 'right', and 'center'. If no default has been defined - by the parent table tag, then default is 'center'. - - - - valign - false - true - java.lang.String - - The horizontal alignment of data in the list's cells. Valid values - are 'top', 'bottom', and 'middle'. If no default has been defined - by the parent table tag, then default is 'middle'. - - - - width - false - true - java.lang.String - - The width of the column, in either pixel or percentage terms - - - - weight - false - true - - The weight of the column. Columns are sized based on their relative weights. - - - - nowrap - false - true - - Sets the nowrap html attribute for the column. Default is to allow wrapping - - - - isRowHeader - false - true - - Whether this column's rows are a row header. Default false. - - - - - - - flyoutform - blackboard.servlet.tags.ngui.FlyoutFormTag - JSP - - - - id - true - true - - - - - header - false - true - - - - - legend - false - true - - - - - showSubmitCancel - false - true - - - - - action - true - true - - - - - type - false - true - - - - - - - - - - miniFlyout - blackboard.servlet.tags.ngui.MiniFlyoutTag - JSP - - Programmatically create/invoke a basic (single input text with cancel/submit actions ) mini flyout control. - - - id - true - true - - id of the mini flyout hyperlink element, the on-click event on which the flyout opens - - - - inputTitle - false - true - - Title value of the input text element in the mini flyout control - - - - linkLabel - false - true - - label or text associated with the mini flyout hyperlinked element, - - - - linkValue - false - true - - value associated with the mini flyout hyperlinked element, - - - - flyoutOptions - false - true - - A valid json string with attributes that match the configuration parameters in the miniFlyout.js - Refer the miniFlyout.js docs for additional information on the configuration attributes for the miniFlyout - If not provided, default values wherever possible will be used for configuration and there wil no auto-wiring - The onClick handler can custom configure the miniFlyout configuration parameters - - - - className - false - true - - Additional className associated with the hyperlink element. A default 'miniFlyout' className is given to each of the - flyout hyperlink elements in order to autowire these links for event listeners on page load - - - - onClick - false - true - - onClick call back js method for the miniFlyout hyperlink element - - - - - - - - hierarchyList - blackboard.servlet.tags.ngui.hierarchylist.HierarchyListTag - scriptless - - This tag generates a simple reorderable list of items whose title, body content, - context menu and short properties need to be rendered in a simple format. - Unlike inventoryList tag, it neither has table look nor support columns and other - features come with a table, such as sorting, paging or headers. Multiple sublists - can be nested within an item to achieve multi-layering. - - - reorderable - false - true - - Whether this list is reorderable. Default: false - - - - reorderingUrl - false - true - - Url that will be posted to persist the drag and drop. - For easy implementation, use a subclass of BaseListRepositioningAction for your server-side code. - If you do not need server-notification (i.e. you just want on-page drag/drop framework and will - deal with the ordering via the order of parameters posted to the server on submit) then you - can specify "noop" as the reordering URL. - - - - reorderType - false - true - A string describing the type of item being reordered (used as part of the title on the accessible repositioning controls). Required if reorderable. - - - contextParameters - false - true - - Query-string formatted string of additional request parameters to pass to the struts - action. - - - - id - false - true - - - - - label - false - true - - If specified then this label will be displayed before the list. If the list is - reorderable then the reorder controls will be beside it. - - - - listClass - false - true - - A CSS class that should be applied to the hierarchy list as a whole. - - - - itemDetailsClass - false - true - - A CSS class that should be applied to any item details section in each row of this list. - If unspecified, defaults to the class "itemDetails". - - - - - - - hierarchyListItem - blackboard.servlet.tags.ngui.hierarchylist.HierarchyListItemTag - scriptless - - This tag represents an item in a hierarchy list. - It can optionally contain a delegateContextMenu tag which will render a - context menu next to the title of the item. The content of the item should be - specified in the body of the tag, and can contain any HTML/other jsp tags. - One or more hierarchyList tag can be added to this tag's body to form multi- - layered list. - - - id - false - true - - An id that uniquely identifies the item (e.g. the database id of the item ). - This will be used to identify the order of the items when reordering them. - - - - title - true - true - - Title of the hierarchy list item. - - - - titleColor - false - true - - Title color of the hierarchy list item. The acceptable values are valid - HTML color names or 6 digit hexcodes optionally starting with # sign, such - as "Black" or "#000000" or "000000". - - - - - - itemDetail - blackboard.servlet.tags.ngui.hierarchylist.ItemDetailTag - empty - - Optional property of an hierarchy list item rendered at the far right for LTR - locale (or far left for RTL one) of item title in "title: value" format. This - tag should be used for rendering of short length properties that need to be - visually separated from item's body content. - - - title - true - true - - Title or label of an item detail. - - - - value - true - true - - Value of an item detail. - - - - - - delegateContextMenu - blackboard.servlet.tags.ngui.DelegateContextMenuTag - scriptless - - ContextMenuRendererDelegate. - The contents of the context menu can be populated either: directly from a nav item, - explicitly using contextMenuItem tags, or dynamically. - The context menu must have at least 1 item in it. -

    - Ordering of the context menu items is also allowed. See the description of the - contextMenu tag for details about ordering. - ]]> - - - menuLinkTitle - false - true - Text for the title attribute of the context menu link. If not specified will default to: Click to see options - - - navItem - false - true - - Handle of the root navigation item to grab the context menu items from. - All children nav items of the specified item will be used to populate the context menu. - - - - dynamic - false - true - Whether this context menu is populated at run time. - - - menuGeneratorURL - false - true - Url that the dynamic options will be loaded from. - - - contextParameters - false - true - Context parameters needed for resolution of href's. - - - orderKey - false - true - - base/config/internal/contextMenuOrder.properties - file to load the context menu item order from. - ]]> - - - - order - false - true - - ContextMenuTag class for the separator and default item id strings. -

    - Example: -

    - <bbData:constants type="blackboard.servlet.tags.ngui.ContextMenuTag"/> -
    - <bbNG:listContextMenu order="sample_item1,${ContextMenuTag['SEPARATOR']},${ContextMenuTag['DEFAULT_ITEM_ID_PREFIX']}"...> - ]]> - - - - - - editToolContextMenu - blackboard.servlet.tags.ngui.EditToolContextMenuTag - scriptless - - This tag creates a context menu that provides a set of actions to take on a - navitem being rendered inside the tools landing page in edit mode. - The context menu must have at least 2 items in it. - - - menuLinkTitle - false - true - Text for the title attribute of the context menu link. If not specified will default to: Click to see options - - - navItem - false - true - - Handle of the root navigation item to grab the context menu items from. - All children nav items of the specified item will be used to populate the context menu. - - - - dynamic - false - true - Whether this context menu is populated at run time. - - - menuGeneratorURL - false - true - Url that the dynamic options will be loaded from. - - - contextParameters - false - true - Context parameters needed for resolution of href's. - - - ifHiddenText - false - true - Text displayed if the item is hidden - - - - - canvas - blackboard.servlet.tags.ngui.canvas.CanvasTag - scriptless - - TODO - - - reorderable - false - true - - Whether this list is reorderable. Default: false - - - - reorderingUrl - false - true - - Url that will be posted to persist the drag and drop. - For easy implementation, use a subclass of BaseListRepositioningAction for your server-side code. - - - - reorderType - false - true - A string describing the type of item being reordered (used as part of the title on the accessible repositioning controls). Required if reorderable. - - - contextParameters - false - true - - Query-string formatted string of additional request parameters to pass to the struts - action. - - - - id - false - true - - - - - emptyMsg - false - true - - The localized text message to be displayed if the given collection is empty. - The default message is "No items found." - - - - mayBeNested - false - true - - Default: true - If you know that this canvas is not going to contain any nested draggable areas - then you can set this parameter to false to speed up the drag/drop UI interaction - - - - - - canvasItem - blackboard.servlet.tags.ngui.canvas.CanvasItemTag - scriptless - - This tag represents an item in a canvas. - It can optionally contain a delegateContextMenu tag which will render a - context menu next to the title of the item. The content of the item should be - specified in the body of the tag, and can contain any HTML/other jsp tags. - One or more canvas tag can be added to this tag's body to form multi- - layered list. - - - id - false - true - - An id that uniquely identifies the item (e.g. the database id of the item ). - This will be used to identify the order of the items when reordering them. - - - - title - true - true - - Title of the canvas list item. - - - - cssClasses - false - true - - Space separated list of css classes to apply to item - - - - reorderableOverride - false - true - - Set this value to override the parent canvas reorderable attribute. The value set is the new - reorderable value. So setting this value to "false" makes this canvasItem not reorderable. - - - - - - canvasDetail - blackboard.servlet.tags.ngui.canvas.CanvasDetailTag - empty - - Optional property of an canvas item rendered at the far right for LTR - locale (or far left for RTL one) of item title in "title: value" format. This - tag should be used for rendering of short length properties that need to be - visually separated from item's body content. - - - title - true - true - - Title or label of an item detail. - - - - value - true - true - - Value of an item detail. - - - - - collapsibleList - blackboard.servlet.tags.ngui.collapsiblelist.CollapsibleListTag - scriptless - - expand/collapse List that performs heavy load of listItems on list expansion - - - id - true - true - - unique value to identify multiple collapseList items on a single page - - - - isDynamic - true - true - - if set to false and will render static content on expand - if set to true , will render dynamic content on expand based on listItemGeneratorURL - - - - listItems - false - true - - lightweight collection of listItems of type collapsibleListItem - It can be empty even when specified. - - - - listItemGeneratorURL - false - true - - URL that performs heavy load for each list Item specified in the listItems attribute - - - - contextParameters - false - true - - Query-string formatted string of additional request parameters to pass to the struts - action. - - - - - - collapsibleListItem - blackboard.servlet.tags.ngui.collapsiblelist.CollapsibleListItemTag - scriptless - - Child items inside the CollapsibleListTag - - - id - true - true - - unique value for each listItem - - - - title - false - true - - title of the collapsed List Item - - - - body - false - true - - arbitrary body html of the expanded list Item - - - - isDisabled - false - true - - default value is false - if set to true the item cannot be expanded - - - - expandOnPageLoad - false - true - - default value is false - if set to true, expands the listItem on pageload - - - - - - - palette - blackboard.servlet.tags.ngui.palette.PaletteTag - JSP - - - Palette actions that may be performed to the palette or its contents may be defined separately - using the palette action item tag. A palette may contain many action items. -

    - Palette items and palette item groups may be passed directly in this tag as collections of beans. -

    - Controlling the palette via javascript: -

    - There are several global functions that the jsp writer may use to control the palette. - They are listed below. Also, see documentation in page.js for usage details. -

      -
    • - page.PaletteController.getDefaultContentsContainerId - - Method returns the string id to the default palette contents container. - Palette contents container is the element that wraps everything below - the palette title area. -
    • -
    • - page.PaletteController.setActivePaletteContentsContainer - - Set a custom palette contents container to the palette controller javascript object. - This is used in the case where palette contents are not added to the default content - container, but in a separate container inside the palette. For example, the course menu - palette in folder view and the Content Collection palette in folder view both - dynamically inserts their own tree containers into the palette in the palette content - area. The active palette contents container is updated via this method so that palette - functionality like expand and collapse palette will work correctly. -
    • -
    • - page.PaletteController.closeAllOtherPalettes - - Close all other palettes on the page, except the specified palette. -
    • -
    • - page.PaletteController.toggleExpandCollapsePalette - - Toggle the display of the specified palette, i.e. close the palette if it's currently - open and open the palette if it's closed. -
    • -
    • - page.PaletteController.collapsePalette - - Close the specified palette. -
    • -
    • - page.PaletteController.expandPalette - - Open the specified palette. -
    • -
    - ]]> -
    - - id - false - true - - Unique value to identify this palette in the page. Must be provided if palette - is reorderable or expand/collapsible. - - - - title - false - true - - The localized text name of this palette - - - - href - false - true - - The link url of this palette's title. If not set, the title will not be linked. - - - - hrefTitle - false - true - - Optional tool-tip text for the link url of the palette's title - - - - imgTitle - false - true - - Optional tool-tip img for the link url of the palette's title - - - - target - false - true - - The target for the link url of this palette's title. This used when href is also set. - - - - style - false - true - - The style to use for the palette. This will be used to determine what CSS styles should be applied - to the palette and its items. - - Acceptable values are string representations of the PaletteTag.Style enum items. The default value is - "coursemenu". - - - - readonly - false - true - - Indicates whether palette actions and palette management features may be performed. If false, those - actions will be blocked. For example, if the course menu palette is read-only, palette items cannot - be added to the palette and existing palette items may not be reordered, etc. Default is false. - - - - reorderable - false - true - - Indicates whether the direct child palette items can be reordered (if not in readonly mode). Default is false. - - - - reorderingUrl - false - true - - The URL to which the changes in palette item order will be persisted to. Required if reorderable is true. - - - - reorderType - false - true - A string describing the type of item being reordered (used as part of the title on the accessible repositioning controls). Required if reorderable. - - - shouldCloseAllOtherPalettesWhenOpen - false - true - Whether to close all other palettes when this palette is expanded. Default value is false. - - - contextParameters - false - true - - Additional URL parameters that will be passed to the reorderingUrl in order to pass contextual information - to the persisting action. - - - - paletteItems - false - true - - Collection of PaletteItem beans to be rendered inside the palette. - - - - paletteItemGroups - false - true - - Collection of PaletteItemGroup beans to be rendered inside the palette. - - - - collapsed - false - true - - Collapse palette at first loading time - - - - expandCollapseId - false - true - - The id of the tag who has js onclick event binding on it (in page.js), for palette collapse/expand - - - - cssClasses - false - true - - Space separated list of css classes to apply to the Palette - - -
    - - - paletteActionItem - blackboard.servlet.tags.ngui.palette.PaletteActionItemTag - JSP - - A palette action item is an action that may be performed to the palette or its contents. - Palette action item tags must be used within the palette tag. - - The action may be blocked and hidden from display if the readonly attribute in the parent - palette tag is set unless overridden via the showWhenReadonly attribute below. - - The action link(s) to the action must be provided either through the href or dropdownItems - attributes (*not* both). Giving the href attribute will directly link the icon while the - dropdownItems will display a dropdown list of action links under the icon. - - Any html specified in the body of the tag will be appended to the item - - - id - false - true - - Unique value to identify this action item in the page. - - - - iconUrl - true - true - - The url of the icon to display for this action item - - - - toolTipText - true - true - - The localized text that should appear as a tool-tip describing the action. - - - - active - false - true - - Whether the item is in the "active" state. Default is false. - - - - primary - false - true - - Whether the item is primary (shown in natural left) or not (shown in natural right). Default is false. - - - - showWhenReadonly - false - true - - Whether the action item should be shown despite the parent palette tag being in readonly mode. Default is false. - - - - href - false - true - - The action item link. - - - - dropdownItems - false - true - - List of ContextMenuItem beans that will appear as a dropdown list of links for this action item. - - - - - - paletteItemGroup - blackboard.servlet.tags.ngui.palette.PaletteItemGroupTag - JSP - - A palette item group is a container that visually distinguishes and associates a list of palette - items by grouping them together. The list of palette items may be either declared in the body of - this tag or passed as a collection to the associated attribute in this tag. - - The group may be either reorderable or loaded dynamically (however, not both) - - The way the group looks depends on several factors. - * Whether the group has a href - * Whether the group has (or will have) any children - - If a group has a href and will not have any children, the group is just displayed as a link - - If a group has a href and has children, the group name will be displayed as a link (to the href) - with an icon on the right that can be clicked to expand/collapse the group. - - If a group doesn't have an href and has children, clicking on the group name will expand/collapse the group. - - - id - true - true - - Unique value to identify this palette item group in the page. - - - - title - true - true - - The localized text name of the palette item group. - - - - href - false - true - - A navigational link on the title text to a content page related to the item group. - - - - hrefTitle - false - true - - Optional tool-tip text for the navigational link on the title text. - - - - imgTitle - false - true - - Optional tool-tip img for the link url of the palette's title - - - - target - false - true - - The target for href, the navigational link on the title text to a content page related to the item group. - - - - reorderable - false - true - - Whether the items in the item group should be repositionable. Default is false. - - - - reorderingUrl - false - true - - The URL to which changes to the order of the group's items will be persisted. Required if reorderable is true. - - - - reorderType - false - true - A string describing the type of item being reordered (used as part of the title on the accessible repositioning controls). Required if reorderable. - - - contextParameters - false - true - - Additional URL parameters that will be passed to the reorderingUrl in order to pass contextual information - to the persisting action. - - - - dynamicLoading - false - true - - Whether the items in the item group are loaded dynamically. Default is false. - - - - dynamicItemGeneratorUrl - false - true - - URL that will be used to dynamically generate the group's items. - - - - paletteItems - false - true - - Collection of PaletteItem beans to be rendered inside the group. - - - - - - paletteItem - blackboard.servlet.tags.ngui.palette.PaletteItemTag - JSP - - A palette item is a single navigational link in the palette. An item can appear - directly within the palette or within palette item groups that visually distinguish - and associate a list of items. Palette item tags must be used within a palette or - palette item group tag. - - The body of this tag will be directly inserted as the body of the item. This means - that the user of this tag will have to take care of any special logic for hiding/displaying - items in readonly mode, etc. - - The id and title attributes must be specified if the palette item is in a reorderable - Palette or PaletteItemGroup. This is so that the accessible repositioning widget can - be populated. - - - id - false - true - - Value that will be passed to the repositioning action that will uniquely identify the - palette item. A good value for this is the database id of the item you're displaying. - - - - title - false - true - - The localized text name of the palette item. Used in the accessible repositioning widget. - - - - className - false - true - - An option CSS class to apply to the item. - - - - - - buttonPalette - blackboard.servlet.tags.ngui.palette.ButtonPaletteTag - empty - - A button palette is a palette that contains a single link. - - - url - true - true - - URL that the button will link to. - - - - label - true - true - - The localized text label of the button. - - - - id - false - true - - An optional id that uniquely identifies the button. - - - - cssClass - false - true - - An optional CSS class to apply to the button. - - - - onClick - false - true - - Optional JavaScript handler to call when the button is clicked. - - - - - - userRoleSelect - blackboard.servlet.tags.ngui.datacollection.fields.UserRoleSelectTag - JSP - - Creates a User Role (primary and secondary specific) MultiSelect tag. This uses the - blackboard.servelet.data.MultiSelectBean object to pass data into the list which is - displayed in the two Select boxes - ]]> - After the form is submitted, "widgetName_left_values" and "widgetName_right_values" - will be a comma-delimited string of values of all options that were in the - respective select box upon form submission - - - sourceTitle - false - true - - Left title is the text that appears on the top left of the widget (Available Roles) - - - - destTitle - false - true - - Right title is the text that appears above top right of the widget (Selected Roles) - - - - primaryTitle - false - true - The title of the Primary Role section field - - - secondaryTitle - true - true - The Title of the Secondary Role selection box - - - sourceName - false - true - - This refers to the name attribute in the select tag for the left multi-select box. - defaults to widgetName_left_select and widgetName_right_select where the - widgetName is replaced by the specified widgetName (or defaulted to "multiselect" - if none is provided) - - - - destName - false - true - - This refers to the name attribute in the select tag for the right multi-select box - - - - primaryInputName - true - true - - The refers to the name attribute of the input field used for primary Role - - - - sourceCollection - true - true - java.util.Collection - - The collection passed to the left Select box. This is a list of MultiSelectBean - objects passed to be displayed on the left select box. The setLabel and setValue - methods should be set for each member of the list. The setIsSeleted can be set in - case some options need to be seleted when the widget first shows up - - - - destCollection - true - true - java.util.Collection - - The collection passed to the right Select box. This is a list of MultiSelectBean - objects passed to be displayed on the left select box. The setLabel and setValue - methods should be set for each member of the list. The setIsSeleted can be set in - case some options need to be seleted when the widget first shows up - - - - primaryRoleName - true - true - - The string which refers to the currently selected primary Role (if any) - - - - primaryRoleId - true - true - - The string which refers to the role_id of the currently selected primary role - - - - formName - true - true - - This is the name of the form in which the multi-select widget is being used - - - - widgetName - false - true - - Widget Name will precede the hidden fields in the form widgetName_left_values and - widgetName_right_values. If widgetName is not specified, the name "multiselect" - will be used as a default - - - - type - false - true - - Can be of type "PORTAL_ROLE" . This type will create the standard multiselect for - portal roles - - - - - - - - okButton - blackboard.servlet.tags.ngui.OkButtonTag - empty - - - The purpose of this button/behavior is navigational only-- the user clicks "OK" and - moves up one level on the breadcrumb trail. This button is generally used on - Landing Pages and Inventory List Pages. See the design framework for more details. - ]]> - - - url - false - true - The URL where the button points. - - - onClick - false - true - The onClick javascript handler for the button. - - - - - button - blackboard.servlet.tags.ngui.ButtonTag - empty - - - - - id - false - true - The id of the link - - - url - false - true - The URL where the button points. - - - onClick - false - true - The onClick javascript handler for the button. - - - label - false - true - The label for the button. - - - imgUrl - false - true - The Image URL to be displayed in the button. If specified, the button will be the icon only, - label being used for the img alt and button title. - - - - target - false - true - The (optional) frame name for the link to open in, if it's a link-type button and - "_self" is not adequate - - - - type - false - true - - ButtonTag.Type.ComponentLevel). - ]]> - - - - - - - summaryView - blackboard.servlet.tags.ngui.summary.SummaryTag - JSP - - - title - true - true - - 360 view top level title. - - - - pagetitle - false - true - - 360 view Page level title. - - - - - - - summaryViewItem - blackboard.servlet.tags.ngui.summary.SummaryTocItemTag - JSP - - - - - title - true - true - Title of the TOC. - - - - - summaryViewSubItem - blackboard.servlet.tags.ngui.summary.SummaryTocSubItemTag - JSP - - - - - title - true - true - Title of the sub-TOC. - - - - - summaryDetail - blackboard.servlet.tags.ngui.summary.SummaryDetailTag - JSP - - - - - title - false - true - Title of the detail. - - - - - summaryDetailSection - blackboard.servlet.tags.ngui.summary.SummaryDetailSectionTag - JSP - - - - - title - false - true - Title of the section. - - - - - summaryProperty - blackboard.servlet.tags.ngui.summary.SummaryPropertyTag - JSP - - - - - property - true - true - Name of the property. - - - value - false - true - Value of the property. - - - bodyValue - false - true - Boolean: Indicating if the value is body content. This overrides the value above. - - - - - metadataList - blackboard.servlet.tags.ngui.list.MetadataListTag - JSP - - The tag serves to generate a list of metadata used for describing the characteristics of some content or data. - This information is displayed in a series of label, value pairs. - Either the item attribute or metadataListItem tags in the body must be provided. - - - baseFieldName - false - true - - Prefix string to the ids of the label and field divs of a metadata. - Default is "meta". This field should be provided and unique between - metadata lists if there are more than one metadata lists in the page. - - - - items - false - true - - List of metadata items (blackboard.servlet.data.ngui.ItemDetail) to loop through to populate the list. - - - - - - metadataListItem - blackboard.servlet.tags.ngui.list.MetadataListItemTag - JSP - - Metadata list items are items within a metadata list. This tag is only for - use within a metadataList tag. The value of the item may be provided - either via the "value" attribute or within the body of the tag, but is not - required. - - - id - false - true - The id for the metadata item. - - - label - true - true - The label for the metadata item. The label should be localized text. - - - value - false - true - - The value of the metadata item. If provided and is text, it should be localized. - - - - labelFor - false - true - - - For example: - <label for="gradingNotestext">Grading Notes</label> - ]]> - - - - - - - tagging - blackboard.servlet.tags.ngui.TaggingTag - empty - - Tag that provides a control for "tagging" an element with keywords ala Flickr. - - - currentTagList - false - true - - A List containing the actual TagData objects that should be displayed as in use - - - - fullTagList - false - true - - A List which indicates the full set of tags (as currently defined) that - can be chosen from in order to add tags to the current element. This - list should include the same tags as defined by the current tag list - (i.e., it should be a superset of the current tag list). If this value - is null/empty, the "select from existing" values will be pulled from - a global javascript list. - - - - showSelectFromExisting - false - true - - Boolean to determine whether the "select from existing" is displayed. - Defaults to true. - - - - removeMsg - false - true - - The message that will be shown to the user when they click to remove - one of the tags currently set. If this value is not provided, a - "generic" remove confirmation message will be used (common.warning.remove - in common.properties). If an empty string is provided, no remove - confirmation message will be shown. - - - - tagClickUrl - false - true - - URL for when the user clicks on the tag name. If specified, the URL will - be parameterized by replacing "@X@TagName@X@" with the tag value for that - particular tag. If not specified, the tag name is not linked. - - - - addJsCallback - false - true - - The name of a JS function that should be called when an add operation - is performed. The method provided should accept a single string value - (the actual tag value) as a parameter. The output of this method will - be ignored and will not effect the flow of execution. - - - - removeJsCallback - false - true - - The name of a JS function that should be called when a remove operation - is performed. The method provided should accept a single string value - (the actual tag value) as a parameter. The output of this method will - be ignored and will not effect the flow of execution. - - - - getMatchingTagsJsCallback - false - true - - The name of a JS function that should be called in order to aynchrnously - fetch all the matching tags for autocompletion suggestions in the add Tag - input box for a given tag input. The method provided should accept a single string value - (the actual tag value) as a parameter. The output of this method will be the set of tags - to be used as suggestions. - - - - fieldName - false - true - - The results of the tagging operation (the list of tags to apply to the - element) can be returned in a hidden form field (as an encoded string). - If this attribute is specified, the tag values will be tracked in a form - field with the given name. Interpretation of the form field value - (encoded string) can be done using TaggingTag.parseFormField(String). If - this attribute is not provided, no form field tracking will be performed - and it is assumed that the tagging process is being monitored via - another mechanism (i.e., callbacks). - - - - containerString - false - true - - Unique string for the thing being tagged. This is necessary so the elements - on the page can be distinguished from one another. If not specified, a - timestamp is used. The containing span for this tagging instance will be - named = ("tags_" + containerString) - - - - outputJavascript - false - true - - Boolean: whether or not to output javascript event handling on each instance - of the tag. Defaults to false. - - - - readOnly - false - true - - Boolean: whether or not the list of tags is read-only. Defaults to false. - - - - normalizeTagValue - false - true - - Boolean: whether or not the tag value should be normalized. Defaults to true. - - - - showAutoCompleteForTags - false - true - - Boolean: whether or not to suggest existing tag values a.k.a autocomplete while adding tags to the current list. - Defaults to true. - - - - showCurrentTags - false - true - - Boolean: whether to display the list of current tags - Defaults to true. - - - - addTagLabel - false - true - - a string value representing the label to be displayed for adding tags - Defaults to Add Tag. - - - - addTagButtonLabel - false - true - - a string value representing the label to displayed for the action button for adding tags - Defaults to Add. - - - - showTagCloud - false - true - - a boolean representing if the tag cloud should be shown when the user clicks on the - choose from existing button in the tagging ui. - If set to true, the fullTagList must contain a weighted taglist. - The weight attribute of TagData must be populated with the - frequency at which each tag is associated to another entity - in the system - - - - - - horizontalList - blackboard.servlet.tags.ngui.HorizontalListTag - JSP - - Display list oriented in vertical rows - - - collection - true - true - - The collection of objects passed to the list to be displayed. - - - - className - true - true - - The fully qualified class name of the objects in the collection. - - - - objectVar - true - true - - Variable name to represent a single object in the collection. - This is used in the list element tags to set the attribute of the - object to display. Coding naming standards should be followed here, - for example, do not have a space in your variable name. - - - - emptyMsg - false - true - - The localized text message to be displayed if the given collection is empty. - The default message is "No items found." - - - - reorderingUrl - false - true - Url that will be posted to persist the drag and drop. For easy implementation, use a subclass of BaseListRepositioningAction for your server-side code. - - - reorderType - false - true - A string describing the type of item being reordered (used as part of the title on the accessible repositioning controls). Required if reorderable. - - - reorderable - false - true - Whether this list is reorderable. Default: false - - - itemIdAccessor - false - true - - Chained list of method names to get a String to be used as an - id for each table row. This attribute must be set for a - reorderable list. If more than one method need to be called in - sequence, "." should be used as a delimiter. The first method - in the attribute will be called against each object in the - collection. Then subsequent methods will be called against the - return value of the previous method call. All the methods - specified in the attribute must be accessible and the return - type of the final method must be String. No argument passing - and type casting will be done during methods invocation. For - example, if your collection object is - "blackboard.data.discussionboard.Conference" and the attribute - is "getId.getExternalString", - Conference.getId().getExternalString() will be invoked using - java reflection. - - - - itemNameAccessor - false - true - - Chained list of method names to get a String to be used as the - name of a reorderable item in accessible reordering controls. - This attribute must be set for a reorderable list. See the - description of reorderableItemIdAccessor attribute. - - - - contextParameters - false - true - - Query-string formatted string of additional request parameters to pass to the struts - action. - - - - - - horizontalListRow - blackboard.servlet.tags.ngui.HorizontalListRowTag - JSP - - Display list oriented in vertical rows - - - name - false - true - - - - label - false - true - - - - isRowHeader - false - true - - - - nowrap - false - true - - - - - - - landingPage - blackboard.servlet.tags.ngui.landingPage.LandingPageTag - JSP - Wrapper for the entire landing page - - - - landingPageColumn - blackboard.servlet.tags.ngui.landingPage.LandingPageColumnTag - JSP - A Column in the landing page - - - - landingPageSection - blackboard.servlet.tags.ngui.landingPage.LandingPageSectionTag - JSP - A section in the landing page. - - navItem - false - true - - The navigation item whose url will be used at the anchor, and whose title - will be the title for the section unless the title attribute is set. - - - - title - false - true - The title of the section displayed as a h3 - - - instructions - false - true - user instructions for the section - - - - - landingPageCaret - blackboard.servlet.tags.ngui.landingPage.LandingPageCaretTag - JSP - - - - - landingPageCaretItem - blackboard.servlet.tags.ngui.landingPage.LandingPageCaretItemTag - JSP - - - navItem - true - true - - The internal nav item handle of the link - - - - onClick - false - true - - The onClick javascript handler for the link. - - - - status - false - true - - The status of the sequence map tab. - C = complete - P = in progress - N = not started - "" = no style - - - - altLabel - false - true - - If you need an alternative label for the link than the one provided by the nav item, - specify it here. - - - - target - false - true - - The target frame/window that the link will open in. Default is no target (the current window) - - - - - - drawer - blackboard.servlet.tags.ngui.DrawerTag - JSP - Renders a form on the page which is the drawer element at the bottom of the page. - Do not nest inside another form. - Must be present in the jsp before an associated inventoryList if used in conjunction with an inventoryList - Also, the inventoryList needs to have shoppingCart set to True in this use case. - Note that if the drawer is used across page refreshes, the data will be stored in window.name. It is usually - not an issue if the drawer is used in its own window (like a pop-up for a picker). If your interaction is only - on one page, you can guarantee window.name will not be impacted by setting useWindowName to 'false'. - - - pickerSessionId - true - true - - Some identifier that avoids collision in case a window is reused (not likely if using pop-ups like in picker). - The sessionId should be used to identify a drawer session quite uniquely, for example: standards picker for content 2 ('sog2') - - - - onSubmit - false - true - - JS function called on submit. If the function returned false, the URL won't be called. Optional. - - - - onDrawerChange - false - true - - JS function called when an item is removed or added from/to the drawer. The 1st parameter is the item itself, and - the 2nd one a boolean (true=added). The item is mutable and can be modified by this function, for example to change - its attribute values or add extra attributes. It is guaranteed to be called prior to the item being displayed/removed - in/from the drawer table. - - - - formActionUrl - false - true - - URL to which the selection is posted. Not required if onSubmit since onSubmit can actually handle the submission logic. Optional. - - - - onCancel - false - true - - JS function called on cancel. If the function returned false, the URL won't be called. Optional. - - - - cancelUrl - false - true - - URL called when cancel is called. Optional. - - - - nonceId - false - true - - ID for the post operation, so that it cannot be replayed. Optional. - - - - title - true - true - - this is displayed as the title next to the the number of elements in the drawer e.g. Selected Standards. - You can choose to specify a placeholder for the "number of elements". The place holder has to be {drawerItemsCounter} - e.g. if title = {drawerItemsCounter} Selected Standards, - will display as 5 Selected Standards. If no placeholder is specified then the counter is always added to - the end of the title (Default) e.g. Selected Standards 5. - - - - useWindowName - false - false - - Indicates whether the drawer data should store the drawer state in window.name when the page is unloaded. - If all your interaction occurs on a single page set this to false (default value). If you set it to true - beware the changes to window.name does not conflict with other usages of that property. - - - - extraButtonLabel - false - true - - Localized label for the Extra Button which appears between Cancel and Submit. Optional. - - - - extraButtonUrl - false - true - - URL called when the Extra Button is clicked. Optional. - - - - onExtraButton - false - true - - JS function called on clicking Extra Button. If the function returned false, the URL won't be called. Optional. - - - - - - drawerColumn - blackboard.servlet.tags.ngui.DrawerColumnTag - empty - - - - - label - true - true - - The header label for the column. - - - - name - true - true - - This name will be used by the js to populate the contents of an individual tabel cell. - - - - accessibilityLabelOnly - false - true - - By default this is false. If set to true, the label on the column will NOT be shown. - Instead, it will be placed in a hidden span so that only screen readers will use it. - - - - - - - nestedList - - blackboard.servlet.tags.ngui.list.NestedListTag - - - blackboard.servlet.tags.ngui.list.InventoryListTei - - JSP - - This is a scaffolding tag for an nested list page and must - contain at least one list element. It creates an nested - list used for displaying a collection of objects and its - attributes. Given a collection of objects, it will nest - the list based on the level. - - - collection - true - true - - The collection of objects passed to the list to be displayed. - - - - className - true - true - - The fully qualified class name of the objects in the collection. - - - - objectVar - true - true - - Variable name to represent a single object in the collection. - This is used in the list element tags to set the attribute of the - object to display. Coding naming standards should be followed here, - for example, do not have a space in your variable name. - - - - levelProperty - true - true - Name of the bean property that will be used to nest list. - - - emptyMsg - false - true - - The localized text message to be displayed if the given collection is empty. - The default message is "No items found." - - - - description - false - true - - The list description that will be used for the summary of the table that - contains this list. The description helps users browsing with accessibility - tools quickly understand what kind of information is in the list table. - The default summary for the table is: "This is a table showing the - attributes of a collection of items." - - - - listName - false - true - - - - - - - - nestedListElement - blackboard.servlet.tags.ngui.list.NestedListElementTag - JSP - - A list element represents a column in the nested list. This list will be displayed based on - the level specified in the levels attribute. If the levels attribute is not specified, the - element will be displayed for the level which don't have any nestedList element. - - - name - true - true - - Name of this list element used by the parent inventory list tag when - determining what the initial sort column is, for example. - - - - label - false - true - The label for this column. The label should be localized text. - - - isRowHeader - false - true - - Boolean attribute indicating that the information in the body of this tag is the most - representative of the object represented in the row (for example, with Name, Age, Weight, and Height, - the Name should be the one with isRowHeader=true). This setting is used to set row header tags as well - as title attributes to check boxes (if any) within the list table for accessibility compliance. The - default value is false. - - - - displayText - false - true - - The nested list will expand/collapse on clicking on the text. - This text will be used in combination with the row header. - - - - showCheckbox - false - true - - Boolean attribute indicating if the checkbox need to be displayed - - - - checkBoxValue - false - true - - Value of the checkbox - - - - checkBoxID - false - true - - Name of the checkbox. If not specified the name and id is "chkbx". - - - - nowrap - false - true - - Sets the nowrap HTML attribute for the column. - The default is true to allow wrapping. - - - - levels - false - true - Comma seperated level attribute. The list element will be displayed only for these levels. - If not specified it will be us the element will be used for all the levels. - - - - isAlignable - false - true - Specify if the item isAlignable. Should be a boolean string like "true", "false". If not specified no image is displayed - - - imgUrl - false - true - Specify if a image needs to be displayed before the Display Text. - - - imgAlt - false - true - Specify if a image alt Message needs to be displayed before the Display Text. - - - id - false - true - Id used for item. If no id is set. - - - - - - checkBoxId - blackboard.servlet.tags.ngui.list.ListCheckboxElementTag - empty - - deprecated]]>. Use the ListCheckboxElementTag instead. - This exists for any 9.0 B2 developer backward compatibility. - Optional property of a list item to render a checkbox next to the - item's title. Typically used to allow a user to select the item in order to - execute an action (eg. deletion) - - - name - true - true - - Name of checkbox. - - - - value - true - true - - Value of an checkbox. - - - - - - customFormBuilder - blackboard.servlet.tags.ngui.customformbuilder.CustomFormBuilderTag - JSP - - Creates a Custom Form Builder widet ( aka buildapage). - - - containerId - true - true - - Id of Custom Form Builder Container. - - - - configMappings - true - true - - An instance of blackboard.servlet.tags.ngui.customformbuilder.CustomFormBuilderConfig. - Custom form configuration to define component type to html widget mappings. - - - - reorderType - false - true - A string describing the type of item being reordered (used as part of the title on the accessible repositioning controls). Required if reorderable. - - - - - customFormBuilderInputText - blackboard.servlet.tags.ngui.customformbuilder.CustomFormBuilderInputTag - empty - - An html input control (eg. textbox, vtbe, textbox and select box) which can be added to a - custom form builder container. Must be used within the CustomFormBuilderTag. - - - shareWithStudents - false - true - - Whether this component can be shared with students. - - - - showDropDown - false - true - - Whether top render a drop down box - - - - componentName - true - true - - Localized name of component. - - - - componentType - true - true - - Type of component. - - - - isSectionHeading - false - true - - Whether this is a section heading component. - - - - isLabelOverridden - false - true - - Whether label has been overridden. - - - - inputTextValue - false - true - - Value of the input text. - - - - id - true - true - - Name of checkbox. - - - - options - false - true - - The options to display if this component is being used as a Select box. - - - - - - customFormBuilderInputSelect - blackboard.servlet.tags.ngui.customformbuilder.CustomFormBuilderInputSelectTag - empty - - An html input control (textbox and select box) which can be added to a - custom form builder container. Must be used within the CustomFormBuilderTag. - - - shareWithStudents - false - true - - Whether this component can be shared with students. - - - - componentName - true - true - - Localized name of component. - - - - componentType - true - true - - Type of component. - - - - isLabelOverridden - false - true - - Whether label has been overridden. - - - - inputMap - true - true - - Value of the input text. - - - - id - true - true - - Name of checkbox. - - - - options - true - true - - The options to display if this component is being used as a Select box. - - - - - - customFormBuilderTextbox - blackboard.servlet.tags.ngui.customformbuilder.CustomFormBuilderTextboxTag - empty - - An html input control (vtbe) which can be added to a - custom form builder container. Must be used within the CustomFormBuilderTag. - - - shareWithStudents - false - true - - Whether this component can be shared with students. - - - - componentName - true - true - - Localized name of component. - - - - isLabelOverridden - false - true - - Whether label has been overridden. - - - - componentType - true - true - - Type of component. - - - - inputContent - true - true - - Value of the vtbe. - - - - id - true - true - - Name of checkbox. - - - - - contentsummary - blackboard.servlet.tags.ngui.ContentSummaryTag - empty - - Renders the summary box of a content - - - content - true - true - - A content that need to be rendered - - - - - - flowListElement - blackboard.servlet.tags.ngui.flowlist.FlowInventoryListItemTag - JSP - - A list element represents a column in the flow list. - - - - - flowInventoryList - - blackboard.servlet.tags.ngui.flowlist.FlowInventoryListTag - - - blackboard.servlet.tags.ngui.list.InventoryListTei - - JSP - - Displays items in a left-to-right flow. - - - collection - true - true - - The collection of objects passed to the list to be displayed. - - - - className - true - true - - The fully qualified class name of the objects in the collection. - - - - objectVar - true - true - - Variable name to represent a single object in the collection. - This is used in the list element tags to set the attribute of the - object to display. Coding naming standards should be followed here, - for example, do not have a space in your variable name. - - - - shoppingCart - false - true - - Indicates whether this list should behave as a shopping cart. In shopping - cart mode the check boxes are coupled with a page state 'the drawer'. When - an element is checked it is added to the drawer, and if removed from the - drawer it is also unchecked from the list. Drawer state survives page refresh - and, once the list is displayed, its check boxes are updated to match the current - state of the drawer. - - - - emptyMsg - false - true - - The localized text message to be displayed if the given collection is empty. - The default message is "No items found." - - - - description - false - true - - The list description that will be used for the summary of the table that - contains this list. The description helps users browsing with accessibility - tools quickly understand what kind of information is in the list table. - The default summary for the table is: "This is a table showing the - attributes of a collection of items." - - - - url - false - true - - Url of current page to be used in inventory list links like sort and paging. - - - - includePageParameters - false - true - - Flag denoting whether to include page parameters in the list urls. When set to false, - only list tag related params will be appended to the provided url or the one obtained - from request. - Default value is true, therefore unless some hidden input should be carried - forward to next pages, such as search parameters, it is recommended to consider - setting this value to false. Also, if a page has input that is too long and should - not be appended to url, such as textbox or formatted text description, or can be - reached from another action as forward, this attribute may need to be set to false. - - - - contextParameters - false - true - - Query-string formatted string of additional request parameters to pass to the struts - action. - - - - showAll - false - true - - Boolean attribute for displaying the all items in the list. The default is false for - showing all items. - - - - renderAjax - false - false - - - - - - listId - false - true - - - - - - - - tabbedPanels - blackboard.servlet.tags.ngui.tabbedpanels.TabbedPanelsTag - JSP - - A panel that represents a tabbed set of pages. If the panel contains only 1 - tab, the tab's contents are displayed without the tab panels. - - - collapsible - false - true - - - - - - showTabPanelIfOnlyOne - false - true - - - - - - expandFirst - false - true - - - - - - persistId - false - true - - - - - - - tabbedPanel - blackboard.servlet.tags.ngui.tabbedpanels.TabbedPanelTag - JSP - - A tab contained by a Tabbed Panel. Must be used with the tabbedPanels tag. - - - title - false - true - - - - - - - showViewRubric - blackboard.servlet.tags.ngui.grid.ShowViewRubricTag - JSP - - Renders View Rubrics control - - - title - false - true - - - - - - columnId - false - true - - gradebook outcome defintion id. - - - - courseId - false - true - - course id - - - - columnTitle - false - true - - title of the gradebook column - - - - viewRubricUrl - true - true - - action to display associated rubrics for the given id - - - - onClickAction - false - true - - onclick actions - - - - - - courseFilesExcludeFoldersPicker - blackboard.servlet.tags.ngui.picker.CourseFilesExcludeFoldersPickerTag - empty - - - - - principal - false - true - Principal to use when launching the picker - - - courseId - false - true - Course id of the home folder to show in the picker. - - - formId - true - true - Id of the form - - - - - rubricPanel - blackboard.servlet.tags.ngui.grid.RubricPanelTag - JSP - - Displays an inline rubric panel showing all the Course/Organization rubrics for a given resource. - If no rubric exists, then the panel is not visible. - - - columnId - true - true - java.lang.String - Content Item for which the rubric should be displayed. - - - items - true - true - Content Item for which the rubric should be displayed. - - - mode - false - true - - - - - partial - false - true - - - - - showEmptyMsg - false - true - - Whether an empty message should be displayed or not if no alignments exist. - Default is false. - - - - showAddButton - false - true - - Whether to show the 'Add Rubrics' button - - - - closeCurrentLightbox - false - true - - A flag to indicate if a sourced lightbox should be closed. - - - - - - - - - - EncodeLabel - blackboard.platform.intl.JsResource - java.lang.String encode( java.lang.String) - - - Example usage: -

    - - <fmt:message var="validate" key="validate.key" bundle="${bundles.some_bundle}" />
    - <bbNG:jsBlock>
    -   <SCRIPT LANGUAGE="Javascript">
    -   function alertMe()
    -   {
    -     alert( "${bbNG:EncodeLabel(validate)}" );
    -   }
    -   </SCRIPT>
    - </bbNG:jsBlock>
    -
    - ]]> -
    - -
    - - - EncodeHtml - blackboard.platform.intl.JsResource - java.lang.String encodeHTML( java.lang.String) - - - Example usage: -

    - - <fmt:message var="validate" key="validate.key" bundle="${bundles.some_bundle}" />
    - <bbNG:jsBlock>
    -   <SCRIPT LANGUAGE="Javascript">
    -   function alertMe(s)
    -   {
    -     alert( s );
    -   }
    -   alertMe('${bbNG:EncodeHtml(validate)}');
    -   </SCRIPT>
    - </bbNG:jsBlock>
    -
    - ]]> -
    -
    - - - HtmlEscape - blackboard.util.TextFormat - java.lang.String escape( java.lang.String ) - - - Example usage: -

    - - <fmt:message var="validate" key="validate.key" bundle="${bundles.some_bundle}" />
    - <a href="#test" title="${bbNG:HtmlEscape(validate)}">Link</a>
    -
    - ]]> -
    -
    - - - CreateExternalIdString - blackboard.persist.PersistUtil - java.lang.String createExternalIdString( blackboard.persist.Id ) - - Generates an id string which contains both data type and key components. - Useful for cases where you must display a list of heterogenous objects and - be able to identify them uniquely later by a single value. - - - - - FilterHtml - blackboard.util.XSSUtil - java.lang.String filter( java.lang.String) - - The FilterJs function provides JSTL-accessible support for filtering HTML - fragments for dangerous Javascript: - - script tags and all event-handlers are disabled. - - object, embed, and applet tags are left unaltered. - - form, input and textarea tags are disabled. - - src, href and similar attributes are filtered. - - - - <% - request.setAttribute( "theHtmlCode", someHtmlCode ); - %> - - - ${bbNG:FilterHtml(theHtmlCode)} - ]]> - - - - - isLeftToRight - blackboard.servlet.util.TagUtil - boolean isLeftToRight() - - Whether current locale uses an LTR language. - Callers should keep a local cache of this value when feasible. - - - - - htmlDirValue - blackboard.servlet.util.TagUtil - java.lang.String getHtmlDirValue() - - Returns "ltr" for Left-to-Right and "rtl" for Right-to-Left. - Used as the value for the html tag's dir attribute for JSP's that are unable to - use the "docTemplate" custom tag. - Callers should keep a local cache of this value when feasible. - - - - - align - blackboard.servlet.util.TagUtil - java.lang.String getAlignAttrValue(java.lang.String) - - This method takes in, as parameter, the semantic or relative value of "natural" or "inverse", - and returns the absolute alignment value of "left" or "right" based on the locale. - For example, if the locale is Right-to-Left (RTL), then for input value of "natural" or "", the method - returns the string literal "right". If input value is "inverse", it returns "left". - Similarly if the locale is Left-to-Right (LTR), "left" is returned for an input value of "natural" or "", - and "right" is returned for "inverse". - If a value other than "natural" or "inverse" is passed in, no translation is performed and the - input value is returned as is. - - - - - formatNameAndValue - blackboard.servlet.util.TagUtil - java.lang.String formatNameAndValue(java.lang.String, java.lang.String) - - Returns a HTML bidirectional aware text consisting of a name value. - In a left to right locale the text will be displayed in a browser as name followed by value. - In a right to left locale the text will be displayed in a browser as value followed by name. - - - - - convertActionOptionsToActionMenuItems - blackboard.servlet.tags.ActionBarTag - - java.util.List convertActionOptionsToActionMenuItems( java.lang.String, java.util.List ) - - - This method should not be used for writing new pages. New pages should create action menu items directly. - ]]> - - - - - stringToCal - blackboard.servlet.util.DatePickerUtil - - Calendar pickerDatetimeStrToCal(java.lang.String) - - - The stringToCal function provides JSTL-accessible support for converting - a date-time string in the internal format the date pickers use into a - Calendar object. - - - - - calendarToString - blackboard.servlet.util.DatePickerUtil - - String pickerDatetimeCalToStr(java.util.Calendar) - - - The calendarToString function provides JSTL-accessible support for converting - a Calendar object to a date-time string in the internal format the date pickers use. - - - - - getThemeBase - blackboard.servlet.util.ThemeUtil - - String getThemeBase(javax.servlet.jsp.PageContext) - - - Determines the path to the current theme's resource directory. - - - - - -
    diff --git a/oeqPrimaryB2/src/main/webapp/taglibs/config/bbNG.xsl b/oeqPrimaryB2/src/main/webapp/taglibs/config/bbNG.xsl deleted file mode 100644 index 6137db2..0000000 --- a/oeqPrimaryB2/src/main/webapp/taglibs/config/bbNG.xsl +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd - 2.1 - - - - - - - - - - - - - - - - - - - - -
    TagBodyAttributes
    - -

    - Description:
    - - ( no description defined for this tag ) - - -

    -

    - Tag Class:
    -
    - -

    -
    - - - - no attributes - - - -
      -
    • - - - - - - - - - - - ( - - required - - - and not real time evaluated - - ) - - -
      - -
    • - -
    -
    -
    - - - -
    -
    - -
    diff --git a/oeqPrimaryB2/src/main/webapp/taglibs/config/struts/struts-bean.tld b/oeqPrimaryB2/src/main/webapp/taglibs/config/struts/struts-bean.tld deleted file mode 100644 index ebcf6dc..0000000 --- a/oeqPrimaryB2/src/main/webapp/taglibs/config/struts/struts-bean.tld +++ /dev/null @@ -1,1153 +0,0 @@ - - - - - 1.3 - 1.2 - bean - http://struts.apache.org/tags-bean - - Note: Some of the features in this taglib are also - available in the JavaServer Pages Standard Tag Library (JSTL). - The Struts team encourages the use of the standard tags over the Struts - specific tags when possible.

    - -

    This tag library contains tags useful in accessing beans and their - properties, as well as defining new beans (based on these accesses) - that are accessible to the remainder of the page via scripting variables - and page scope attributes. Convenient mechanisms to create new beans - based on the value of request cookies, headers, and parameters are also - provided.

    - -

    Many of the tags in this tag library will throw a - JspException at runtime when they are utilized incorrectly - (such as when you specify an invalid combination of tag attributes). JSP - allows you to declare an "error page" in the <%@ page %> - directive. If you wish to process the actual exception that caused the - problem, it is passed to the error page as a request attribute under key - org.apache.struts.action.EXCEPTION.

    - - ]]> -
    - - cookie - org.apache.struts.taglib.bean.CookieTag - org.apache.struts.taglib.bean.CookieTei - empty - - - Define a scripting variable based on the value(s) of the specified - request cookie. -

    - -

    Retrieve the value of the specified request cookie (as a single - value or multiple values, depending on the multiple attribute), - and define the result as a page scope attribute of type Cookie - (if multiple is not specified) or Cookie[] - (if multiple is specified).

    - -

    If no cookie with the specified name can be located, and no default - value is specified, a request time exception will be thrown.

    - ]]> -
    - - id - true - false - - Specifies the name of the scripting variable (and associated page - scope attribute) that will be made available with the value of the - specified request cookie.

    - ]]> -
    -
    - - multiple - false - true - - If any arbitrary value for this attribute is specified, causes all - matching cookies to be accumulated and stored into a bean of type - Cookie[]. If not specified, the first value for the - specified cookie will be retrieved as a value of type - Cookie.

    - ]]> -
    -
    - - name - true - true - - Specifies the name of the request cookie whose value, or values, - is to be retrieved.

    - ]]> -
    -
    - - value - false - true - - The default cookie value to return if no cookie with the - specified name was included in this request.

    - ]]> -
    -
    -
    - - define - org.apache.struts.taglib.bean.DefineTag - org.apache.struts.taglib.bean.DefineTei - JSP - - - Define a scripting variable based on the value(s) of the specified - bean property. -

    - -

    Create a new attribute (in the scope specified by the - toScope property, if any), and a corresponding scripting - variable, both of which are named by the value of the id - attribute. The corresponding value to which this new attribute (and - scripting variable) is set are specified via use of exactly one of the - following approaches (trying to use more than one will result in a - JspException being thrown):

    -
      -
    • Specify a name attribute (plus optional - property and scope attributes) - - The created attribute and scripting variable will be of the type of the - retrieved JavaBean property, unless it is a Java primitive type, - in which case it will be wrapped in the appropriate wrapper class - (i.e. int is wrapped by java.lang.Integer).
    • -
    • Specify a value attribute - The created attribute and - scripting variable will be of type java.lang.String, - set to the value of this attribute.
    • -
    • Specify nested body content - The created attribute and scripting - variable will be of type java.lang.String, set to - the value of the nested body content.
    • -
    - -

    If a problem occurs while retrieving the specified bean property, a - request time exception will be thrown.

    - -

    The <bean:define> tag differs from - <jsp:useBean> in several ways, including:

    -
      -
    • Unconditionally creates (or replaces) a bean under the - specified identifier.
    • -
    • Can create a bean with the value returned by a property getter - of a different bean (including properties referenced with a - nested and/or indexed property name).
    • -
    • Can create a bean whose contents is a literal string (or the result - of a runtime expression) specified by the value - attribute.
    • -
    • Does not support nested content (such as - <jsp:setProperty> tags) that are only executed - if a bean was actually created.
    • -
    - -

    USAGE NOTE - There is a restriction in the JSP 1.1 - Specification that disallows using the same value for an id - attribute more than once in a single JSP page. Therefore, you will not - be able to use <bean:define> for the same bean - name more than once in a single page.

    - -

    USAGE NOTE - If you use another tag to create the - body content (e.g. bean:write), that tag must return a non-empty String. - An empty String equates to an empty body or a null String, and a new - scripting variable cannot be defined as null. Your bean must return a - non-empty String, or the define tag must be wrapped within a logic tag - to test for an empty or null value.

    - -

    USAGE NOTE - You cannot use bean:define to instantiate - a DynaActionForm (type="org.apache.struts.action.DynaActionForm") with - the properties specified in the struts-config. The mechanics of creating - the dyna-properties is complex and cannot be handled by a no-argument - constructor. If you need to create an ActionForm this way, you must use - a conventional ActionForm. -

    - -

    See the Bean Developer's Guide section on - - bean creation for more information about these differences, as well - as alternative approaches to introducing beans into a JSP page.

    - ]]> -
    - - id - true - false - - Specifies the name of the scripting variable (and associated page - scope attribute) that will be made available with the value of the - specified property.

    - ]]> -
    -
    - - name - false - true - - Specifies the attribute name of the bean whose property is accessed - to define a new page scope attribute (if property is also - specified) or the attribute name of the bean that is duplicated with - the new reference created by this tag (if property is not - also specified). This attribute is required unless you specify - a value attribute or nested body content.

    - ]]> -
    -
    - - property - false - true - - Specifies the name of the property to be accessed on the bean - specified by name. This value may be a simple, indexed, - or nested property reference expression. If not specified, the bean - identified by name is given a new reference identified by - id.

    - ]]> -
    -
    - - scope - false - true - - Specifies the variable scope searched to retrieve the bean specified - by name. If not specified, the default rules applied by - PageContext.findAttribute() are applied.

    - ]]> -
    -
    - - toScope - false - true - - Specifies the variable scope into which the newly defined bean will - be created. If not specified, the bean will be created in - page scope.

    - ]]> -
    -
    - - type - false - true - - Specifies the fully qualified class name of the value to be exposed - as the id attribute.

    - ]]> -
    -
    - - value - false - true - - The java.lang.String value to which the exposed bean - should be set. This attribute is required unless you specify the - name attribute or nested body content.

    - ]]> -
    -
    -
    - - header - org.apache.struts.taglib.bean.HeaderTag - org.apache.struts.taglib.bean.HeaderTei - empty - - - Define a scripting variable based on the value(s) of the specified - request header. -

    - -

    Retrieve the value of the specified request header (as a single - value or multiple values, depending on the multiple attribute), - and define the result as a page scope attribute of type String - (if multiple is not specified) or String[] - (if multiple is specified).

    - -

    If no header with the specified name can be located, and no default - value is specified, a request time exception will be thrown.

    - ]]> -
    - - id - true - false - - Specifies the name of the scripting variable (and associated page - scope attribute) that will be made available with the value of the - specified request header.

    - ]]> -
    -
    - - multiple - false - true - - If any arbitrary value for this attribute is specified, causes a call - to HttpServletRequest.getHeaders() and a definition of the - result as a bean of type String[]. Otherwise, - HttpServletRequest.getHeader() will be called, and a - definition of the result as a bean of type String - will be performed.

    - ]]> -
    -
    - - name - true - true - - Specifies the name of the request header whose value, or values, - is to be retrieved.

    - ]]> -
    -
    - - value - false - true - - The default header value to return if no header with the - specified name was included in this request.

    - ]]> -
    -
    -
    - - include - org.apache.struts.taglib.bean.IncludeTag - org.apache.struts.taglib.bean.IncludeTei - empty - - - Load the response from a dynamic application request and make it available - as a bean. -

    - -

    Perform an internal dispatch to the specified application component - (or external URL) - and make the response data from that request available as a bean of - type String. This tag has a function similar to that of - the standard <jsp:include> tag, except that the - response data is stored in a page scope attribute instead of being - written to the output stream. If the current request is part of a - session, the generated request for the include will also include the - session identifier (and thus be part of the same session).

    - -

    The URL used to access the specified application component is - calculated based on which of the following attributes you specify - (you must specify exactly one of them):

    -
      -
    • forward - Use the value of this attribute as the name - of a global ActionForward to be looked up, and - use the module-relative or context-relative URI found there.
    • -
    • href - Use the value of this attribute unchanged (since - this might link to a resource external to the application, the - session identifier is not included.
    • -
    • page - Use the value of this attribute as an - module-relative URI to the desired resource.
    • -
    - ]]> -
    - - anchor - false - true - - Optional anchor tag ("#xxx") to be added to the generated - hyperlink. Specify this value without any - "#" character.

    - ]]> -
    -
    - - forward - false - true - - Logical name of a global ActionForward that contains - the actual content-relative URI of the resource to be included.

    - ]]> -
    -
    - - href - false - true - - Absolute URL (including the appropriate protocol prefix such as - "http:") of the resource to be included. Because this URL could be - external to the current web application, the session identifier will - not be included in the request.

    - ]]> -
    -
    - - id - true - false - - Specifies the name of the scripting variable (and associated page - scope attribute) that will be made available with the value of the - specified web application resource.

    - ]]> -
    -
    - - page - false - true - - Module-relative URI (starting with a '/') of the web application - resource to be included.

    - ]]> -
    -
    - - transaction - false - true - boolean - - Set to true if you want the current - transaction control token included in the generated - URL for this include.

    - ]]> -
    -
    -
    - - message - org.apache.struts.taglib.bean.MessageTag - empty - - - Render an internationalized message string to the response. -

    - -

    Retrieves an internationalized message for the specified locale, - using the specified message key, and write it to the output stream. - Up to five parametric replacements (such as "{0}") may be specified.

    - -

    The message key may be specified directly, using the key - attribute, or indirectly, using the name and - property attributes to obtain it from a bean.

    - -

    - JSTL: The equivalent JSTL tag is <fmt:message>. For example, -
    - - <fmt:message key="my.msg.key"> - <fmt:param value="replacement text"/> - </fmt:message> - -

    - ]]> -
    - - arg0 - false - true - - First parametric replacement value, if any.

    - ]]> -
    -
    - - arg1 - false - true - - Second parametric replacement value, if any.

    - ]]> -
    -
    - - arg2 - false - true - - Third parametric replacement value, if any.

    - ]]> -
    -
    - - arg3 - false - true - - Fourth parametric replacement value, if any.

    - ]]> -
    -
    - - arg4 - false - true - - Fifth parametric replacement value, if any.

    - ]]> -
    -
    - - bundle - false - true - - The name of the application scope bean under which the - MessageResources object containing our messages - is stored.

    - ]]> -
    -
    - - key - false - true - - The message key of the requested message, which must have - a corresponding value in the message resources. If not specified, - the key is obtained from the name and - property attributes.

    - ]]> -
    -
    - - locale - false - true - - The name of the session scope bean under which our currently - selected Locale object is stored.

    - ]]> -
    -
    - - name - false - true - - Specifies the attribute name of the bean whose property is accessed - to retrieve the value specified by property (if - specified). If property is not specified, the value of - this bean itself will be used as the message resource key.

    - ]]> -
    -
    - - property - false - true - - Specifies the name of the property to be accessed on the bean - specified by name. This value may be a simple, indexed, - or nested property reference expression. If not specified, the value - of the bean identified by name will itself be used as the - message resource key.

    - ]]> -
    -
    - - scope - false - true - - Specifies the variable scope searched to retrieve the bean specified - by name. If not specified, the default rules applied by - PageContext.findAttribute() are applied.

    - ]]> -
    -
    -
    - - page - org.apache.struts.taglib.bean.PageTag - org.apache.struts.taglib.bean.PageTei - empty - - - Expose a specified item from the page context as a bean. -

    - -

    Retrieve the value of the specified item from the page context - for this page, and define it as a scripting variable, and a page scope - attribute accessible to the remainder of the current page.

    - -

    If a problem occurs while retrieving the specified configuration - object, a request time exception will be thrown.

    - ]]> -
    - - id - true - false - - Specifies the name of the scripting variable (and associated - page scope attribute) that will be made available with the value of - the specified page context property.

    - ]]> -
    -
    - - property - true - true - - Name of the property from our page context to be retrieved and - exposed. Must be one of application, config, - request, response, or session. -

    - ]]> -
    -
    -
    - - parameter - org.apache.struts.taglib.bean.ParameterTag - org.apache.struts.taglib.bean.ParameterTei - empty - - - Define a scripting variable based on the value(s) of the specified - request parameter. -

    - -

    Retrieve the value of the specified request parameter (as a single - value or multiple values, depending on the multiple attribute), - and define the result as a page scope attribute of type String - (if multiple is not specified) or String[] - (if multiple is specified).

    - -

    If no request parameter with the specified name can be located, and - no default value is specified, a request time exception will be thrown.

    - ]]> -
    - - id - true - false - - Specifies the name of the scripting variable (and associated page - scope attribute) that will be made available with the value of the - specified request parameter.

    - ]]> -
    -
    - - multiple - false - true - - If any arbitrary value for this attribute is specified, causes a call - to ServletRequest.getParameterValues() and a definition of - the result as a bean of type String[]. Otherwise, - ServletRequest.getParameter() will be called, and a - definition of the result as a bean of type String - will be performed.

    - ]]> -
    -
    - - name - true - true - - Specifies the name of the request parameter whose value, or values, - is to be retrieved.

    - ]]> -
    -
    - - value - false - true - - The default parameter value to return if no parameter with the - specified name was included in this request.

    - ]]> -
    -
    -
    - - resource - org.apache.struts.taglib.bean.ResourceTag - org.apache.struts.taglib.bean.ResourceTei - empty - - - Load a web application resource and make it available as a bean. -

    - -

    Retrieve the value of the specified web application resource, and make - it available as either a InputStream or a String, - depending on the value of the input attribute.

    - -

    If a problem occurs while retrieving the specified resource, a - request time exception will be thrown.

    - ]]> -
    - - id - true - false - - Specifies the name of the scripting variable (and associated page - scope attribute) that will be made available with the value of the - specified web application resource.

    - ]]> -
    -
    - - input - false - true - - If any arbitrary value for this attribute is specified, the resource - will be made available as an InputStream. If this - attribute is not specified, the resource will be made available - as a String.

    - ]]> -
    -
    - - name - true - true - - Module-relative name (starting with a '/') of the web application - resource to be loaded and made available.

    - ]]> -
    -
    -
    - - size - org.apache.struts.taglib.bean.SizeTag - org.apache.struts.taglib.bean.SizeTei - empty - - - Define a bean containing the number of elements in a Collection or Map. -

    - -

    Given a reference to an array, Collection or Map, creates a new bean, of - type java.lang.Integer, whose value is the number of elements - in that collection. You can specify the collection to be counted in any - one of the following ways:

    -
      -
    • As a runtime expression specified as the value of the - collection attribute.
    • -
    • As a JSP bean specified by the name attribute.
    • -
    • As the property, specified by the property attribute, - of the JSP bean specified by the name attribute.
    • -
    - ]]> -
    - - collection - false - true - java.lang.Object - - A runtime expression that evaluates to an array, a Collection, or - a Map.

    - ]]> -
    -
    - - id - true - false - - The name of a page scope JSP bean, of type - java.lang.Integer, that will be created to contain the - size of the underlying collection being counted.

    - ]]> -
    -
    - - name - false - true - - The name of the JSP bean (optionally constrained to the scope - specified by the scope attribute) that contains the - collection to be counted (if property is not specified), - or whose property getter is called to return the collection to be - counted (if property is specified.

    - ]]> -
    -
    - - property - false - true - - The name of the property, of the bean specified by the - name attribute, whose getter method will return the - collection to be counted.

    - ]]> -
    -
    - - scope - false - true - - The bean scope within which to search for the JSP bean specified - by the name attribute. If not specified, the available - scopes are searched in ascending sequence.

    - ]]> -
    -
    -
    - - struts - org.apache.struts.taglib.bean.StrutsTag - org.apache.struts.taglib.bean.StrutsTei - empty - - - Expose a named Struts internal configuration object as a bean. -

    - -

    Retrieve the value of the specified Struts internal configuration - object, and define it as a scripting variable and as a page scope - attribute accessible to the remainder of the current page. You must - specify exactly one of the formBean, forward, - and mapping attributes to select the configuration object - to be exposed.

    - -

    If a problem occurs while retrieving the specified configuration - object, a request time exception will be thrown.

    - ]]> -
    - - id - true - false - - Specifies the name of the scripting variable (and associated - page scope attribute) that will be made available with the value of - the specified Struts internal configuration object.

    - ]]> -
    -
    - - formBean - false - true - - Specifies the name of the Struts ActionFormBean - definition object to be exposed.

    - ]]> -
    -
    - - forward - false - true - - Specifies the name of the global Struts ActionForward - definition object to be exposed.

    - ]]> -
    -
    - - mapping - false - true - - Specifies the matching path of the Struts ActionMapping - definition object to be exposed.

    - ]]> -
    -
    -
    - - write - org.apache.struts.taglib.bean.WriteTag - empty - - - Render the value of the specified bean property to the current - JspWriter. -

    - -

    Retrieve the value of the specified bean property, and render it to the - current JspWriter as a String by the ways:

    -
      -
    • If format attribute exists then value will be formatted on base of format - string from format attribute and default system locale.
    • -
    • If in resources exists format string for value data type (view format - attribute description) then value will be formatted on base of format string - from resources. Resources bundle and target locale can be specified with - bundle and locale attributes. If nothing specified then - default resource bundle and current user locale will be used.
    • -
    • If there is a PropertyEditor configured for the property value's class, the - getAsText() method will be called.
    • -
    • Otherwise, the usual toString() conversions will be applied.
    • -
    -

    When a format string is provided, numeric values are formatted using the - java.text.DecimalFormat class; if the format string came from - a resource, the applyLocalisedPattern() method is used, and - applyPattern() is used otherwise. Dates are formatted using - the SimpleDateFormat class. For details of the specific format - patterns, please see the Javadocs for those classes.

    -

    If a problem occurs while retrieving the specified bean property, a - request time exception will be thrown.

    - ]]> -
    - - bundle - false - true - - The name of the application scope bean under which the - MessageResources object containing our messages - is stored.

    - ]]> -
    -
    - - filter - false - true - boolean - - If this attribute is set to true, the rendered property - value will be filtered for characters that are sensitive in HTML, and any - such characters will be replaced by their entity equivalents.

    - ]]> -
    -
    - - format - false - true - - Specifies the format string to use to convert bean or property value - to the String. If nothing specified, then default format - string for value data type will be searched in message resources by - according key.

    - - ]]> -
    -
    - - formatKey - false - true - - Specifies the key to search format string in application resources.

    - ]]> -
    -
    - - ignore - false - true - boolean - - If this attribute is set to true, and the bean specified - by the name and scope attributes does not - exist, simply return without writing anything. If this attribute is - set to false, a runtime exception to be thrown, - consistent with the other tags in this tag library.

    - ]]> -
    -
    - - locale - false - true - - The name of the session scope bean under which our currently - selected Locale object is stored.

    - ]]> -
    -
    - - name - true - true - - Specifies the attribute name of the bean whose property is accessed - to retrieve the value specified by property (if - specified). If property is not specified, the value of - this bean itself will be rendered.

    - ]]> -
    -
    - - property - false - true - - Specifies the name of the property to be accessed on the bean - specified by name. This value may be a simple, indexed, - or nested property reference expression. If not specified, the bean - identified by name will itself be rendered. If the - specified property returns null, no output will be rendered.

    - ]]> -
    -
    - - scope - false - true - - Specifies the variable scope searched to retrieve the bean specified - by name. If not specified, the default rules applied by - PageContext.findAttribute() are applied.

    - ]]> -
    -
    -
    -
    - - - diff --git a/oeqPrimaryB2/src/main/webapp/taglibs/config/struts/struts-html.tld b/oeqPrimaryB2/src/main/webapp/taglibs/config/struts/struts-html.tld deleted file mode 100644 index e6ae871..0000000 --- a/oeqPrimaryB2/src/main/webapp/taglibs/config/struts/struts-html.tld +++ /dev/null @@ -1,9283 +0,0 @@ - - - - - 1.3 - 1.2 - html - http://struts.apache.org/tags-html - - - This taglib contains tags used to create struts - input forms, as well as other tags generally useful - in the creation of HTML-based user interfaces. -

    - -

    Many of the tags in this tag library will throw a - JspException at runtime when they are utilized incorrectly - (such as when you specify an invalid combination of tag attributes). JSP - allows you to declare an "error page" in the <%@ page %> - directive. If you wish to process the actual exception that caused the - problem, it is passed to the error page as a request attribute under key - org.apache.struts.action.EXCEPTION.

    - ]]> -
    - - base - org.apache.struts.taglib.html.BaseTag - empty - - Render an HTML <base> Element

    - -

    Renders an HTML <base> element with an - href attribute pointing to the absolute location of - the enclosing JSP page. This tag is valid only when nested inside - an HTML <head> element.

    - -

    This tag is useful because it allows you to use relative URL - references in the page that are calculated based on the URL of the - page itself, rather than the URL to which the most recent submit - took place (which is where the browser would normally resolve - relative references against).

    - ]]> -
    - - target - false - true - - The window target for this base reference.

    - ]]> -
    -
    - - server - false - true - - The server name to use instead of request.getServerName().

    - ]]> -
    -
    - - ref - false - true - - The reference from which the base uri will created. Possible values are: -

    -
      -
    • page - The base uri will be the jsp page location. (default)
    • -
    • site - The base uri will be the application context path.
    • -
    - -
    Since:
    -
    Struts 1.3
    - - ]]> -
    -
    -
    - - button - org.apache.struts.taglib.html.ButtonTag - - Render A Button Input Field

    - -

    - Renders an HTML <input> element of type - button, populated from the specified value or the - content of this tag body. This tag is only valid when nested - inside a form tag body. -

    -

    - If a graphical button is needed (a button with an image), then the - <html:image> tag is more appropriate. -

    - ]]> -
    - - accesskey - false - true - - The keyboard character used to move focus immediately to this - element.

    - ]]> -
    -
    - - alt - false - true - - The alternate text for this element.

    - ]]> -
    -
    - - altKey - false - true - - The message resources key of the alternate text for this - element.

    - ]]> -
    -
    - - bundle - false - true - - The servlet context attributes key for the MessageResources - instance to use. If not specified, defaults to the - application resources configured for our action servlet.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - disabled - false - true - boolean - - Set to true if this input field should be - disabled.

    - ]]> -
    -
    - - indexed - false - true - boolean - - Valid only inside of logic:iterate tag. - If true then name of the html tag will be rendered as - "propertyName[34]". Number in brackets will be generated for - every iteration and taken from ancestor logic:iterate tag.

    - ]]> -
    -
    - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - onblur - false - true - - - - - - onchange - false - true - - - - - - onclick - false - true - - - - - - ondblclick - false - true - - - - - - onfocus - false - true - - - - - - onkeydown - false - true - - - - - - onkeypress - false - true - - - - - - onkeyup - false - true - - - - - - onmousedown - false - true - - - - - - onmousemove - false - true - - - - - - onmouseout - false - true - - - - - - onmouseover - false - true - - - - - - onmouseup - false - true - - - - - - property - true - true - - - - - - style - false - true - - - - - - styleClass - false - true - - - - - - styleId - false - true - - - - - - tabindex - false - true - - - - - - title - false - true - - The advisory title for this element.

    - ]]> -
    -
    - - titleKey - false - true - - The message resources key for the advisory title - for this element.

    - ]]> -
    -
    - - value - false - true - - - - -
    - - cancel - org.apache.struts.taglib.html.CancelTag - - - Render a Cancel Button -

    - -

    - Renders an HTML <input> element of type submit. This tag is only - valid when nested inside a form tag body. Pressing of this submit - button causes the action servlet to bypass calling the associated - form bean validate() method. The action is called normally. -

    - ]]> -
    - - accesskey - false - true - - - - - - alt - false - true - - The alternate text for this element.

    - ]]> -
    -
    - - altKey - false - true - - The message resources key of the alternate text for this - element.

    - ]]> -
    -
    - - bundle - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - disabled - false - true - boolean - - true if this input field should be - disabled. - ]]> - - - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - onblur - false - true - - - - - - onchange - false - true - - - - - - onclick - false - true - - - - - - ondblclick - false - true - - - - - - onfocus - false - true - - - - - - onkeydown - false - true - - - - - - onkeypress - false - true - - - - - - onkeyup - false - true - - - - - - onmousedown - false - true - - - - - - onmousemove - false - true - - - - - - onmouseout - false - true - - - - - - onmouseover - false - true - - - - - - onmouseup - false - true - - - - - - property - false - true - - WARNING - If you set this attribute to a - value other than the default, this will NOT be - recognized as the cancel key by the Struts controller servlet - or the Action.isCancelled() method. You will - need to do your own cancel detection. - ]]> - - - - style - false - true - - - - - - styleClass - false - true - - - - - - styleId - false - true - - - - - - tabindex - false - true - - - - - - title - false - true - - The advisory title for this element.

    - ]]> -
    -
    - - titleKey - false - true - - The message resources key for the advisory title - for this element.

    - ]]> -
    -
    - - value - false - true - - - - -
    - - checkbox - org.apache.struts.taglib.html.CheckboxTag - - - Render A Checkbox Input Field -

    - -

    Renders an HTML <input> element of type - checkbox, populated from the specified - value or the specified property of the bean associated - with our current form. This tag is only valid when - nested inside a form tag body.

    - -

    NOTE: The underlying property value - associated with this field should be of type boolean, - and any value you specify should correspond to one - of the Strings that indicate a true value ("true", "yes", or - "on"). If you wish to utilize a set of related String values, - consider using the multibox tag.

    - -

    WARNING: In order to correctly - recognize unchecked checkboxes, the - ActionForm bean associated with this form - must include a statement setting the corresponding - boolean property to false in the - reset() method.

    - ]]> -
    - - accesskey - false - true - - - - - - alt - false - true - - The alternate text for this element.

    - ]]> -
    -
    - - altKey - false - true - - The message resources key of the alternate text for this - element.

    - ]]> -
    -
    - - bundle - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - disabled - false - true - boolean - - true if this input field should be - disabled. - ]]> - - - - errorKey - false - true - - Name of the bean (in any scope) under which our error messages - have been stored. If not present, the name specified by the - Globals.ERROR_KEY constant string will be used.

    - -

    N.B. This is used in conjunction with the - errorStyle, errorStyleClass and - errorStyleId attributes and should be set to - the same value as the name attribute on the - <html:errors/> tag.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyle - false - true - - CSS styles to be applied to this HTML element if - an error exists for it.

    - -

    N.B. If present, this overrides the - style attribute in the event of an error.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleClass - false - true - - CSS stylesheet class to be applied to this HTML element if - an error exists for it (renders a "class" attribute).

    - -

    N.B. If present, this overrides the - styleClass attribute in the event of an error.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleId - false - true - - Identifier to be assigned to this HTML element if - an error exists for it (renders an "id" attribute).

    - -

    N.B. If present, this overrides the - styleId attribute in the event of an error.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - indexed - false - true - boolean - - logic:iterate tag. - If true then name of the html tag will be rendered as - "id[34].propertyName". Number in brackets will be generated - for every iteration and taken from ancestor logic:iterate tag. - ]]> - - - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - name - false - true - - - - - - onblur - false - true - - - - - - onchange - false - true - - - - - - onclick - false - true - - - - - - ondblclick - false - true - - - - - - onfocus - false - true - - - - - - onkeydown - false - true - - - - - - onkeypress - false - true - - - - - - onkeyup - false - true - - - - - - onmousedown - false - true - - - - - - onmousemove - false - true - - - - - - onmouseout - false - true - - - - - - onmouseover - false - true - - - - - - onmouseup - false - true - - - - - - property - true - true - - - - - - style - false - true - - CSS styles to be applied to this HTML element.

    - -

    N.B. If present, the errorStyle - overrides this attribute in the event of an error for the element.

    - ]]> -
    -
    - - styleClass - false - true - - CSS stylesheet class to be applied to this HTML element - (renders a "class" attribute).

    - -

    N.B. If present, the errorStyleClass - overrides this attribute in the event of an error for the element.

    - ]]> -
    -
    - - styleId - false - true - - Identifier to be assigned to this HTML element (renders - an "id" attribute).

    - -

    N.B. If present, the errorStyleId - overrides this attribute in the event of an error for the element.

    - ]]> -
    -
    - - tabindex - false - true - - - - - - title - false - true - - The advisory title for this element.

    - ]]> -
    -
    - - titleKey - false - true - - The message resources key for the advisory title - for this element.

    - ]]> -
    -
    - - value - false - true - - - - -
    - - errors - org.apache.struts.taglib.html.ErrorsTag - empty - - - Conditionally display a set of accumulated error messages. -

    - -

    Displays a set of error messages prepared by a business - logic component and stored as an ActionMessages - object, an ActionErrors - object, a String, or a String array in any scope. If - such a bean is not found, nothing will be rendered.

    - -

    In order to use this tag successfully, you must have - defined an application scope MessageResources - bean under the default attribute name, with optional - definitions of message keys specified in the following - attributes:

    -
      -
    • header - Text that will be rendered - before the error messages list. Typically, this message text - will end with <ul> to start the - error messages list (default "errors.header").
    • -
    • footer - Text that will be rendered - after the error messages list. Typically, this message text - will begin with </ul> to end the error - messages list (default "errors.footer").
    • -
    • prefix - Text that will be rendered - before each individual error in the list (default "errors.prefix").
    • -
    • suffix - Text that will be rendered - after each individual error in the list (default "errors.suffix").
    • -
    - -

    See the messages tag for an alternative to this tag that - does not rely on HTML in your MessageResources.

    - - ]]> -
    - - bundle - false - true - - - - - - footer - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - header - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - locale - false - true - - - - - - name - false - true - - Globals.ERROR_KEY constant string will be used. - ]]> - - - - prefix - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - property - false - true - - - - - - suffix - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    -
    - - file - org.apache.struts.taglib.html.FileTag - - - Render A File Select Input Field -

    - -

    - Renders an HTML <input> element of type file, defaulting to - the specified value or the specified property of the bean - associated with our current form. This tag is only valid when - nested inside a form tag body. -

    -

    - As with the corresponding HTML <input> element, the - enclosing form element must specify "POST" for the method - attribute, and "multipart/form-data" for the enctype - attribute. For example: -

    -
    -    <html:form method="POST" enctype="multipart/form-data">
    -        <html:file property="theFile" />
    -    </html:form>
    - -

    - WARNING: In order to correctly recognize uploaded files, the ActionForm bean - associated with this form must include a statement setting the corresponding - org.apache.struts.upload.FormFile property to null in the reset() method. -

    - ]]> -
    - - accesskey - false - true - - - - - - accept - false - true - - - - - - alt - false - true - - The alternate text for this element.

    - ]]> -
    -
    - - altKey - false - true - - The message resources key of the alternate text for this - element.

    - ]]> -
    -
    - - bundle - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - disabled - false - true - boolean - - true if this input field should be - disabled. - ]]> - - - - errorKey - false - true - - Name of the bean (in any scope) under which our error messages - have been stored. If not present, the name specified by the - Globals.ERROR_KEY constant string will be used.

    - -

    N.B. This is used in conjunction with the - errorStyle, errorStyleClass and - errorStyleId attributes and should be set to - the same value as the name attribute on the - <html:errors/> tag.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyle - false - true - - CSS styles to be applied to this HTML element if - an error exists for it.

    - -

    N.B. If present, this overrides the - style attribute in the event of an error.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleClass - false - true - - CSS stylesheet class to be applied to this HTML element if - an error exists for it (renders a "class" attribute).

    - -

    N.B. If present, this overrides the - styleClass attribute in the event of an error.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleId - false - true - - Identifier to be assigned to this HTML element if - an error exists for it (renders an "id" attribute).

    - -

    N.B. If present, this overrides the - styleId attribute in the event of an error.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - indexed - false - true - boolean - - logic:iterate tag. - If true then name of the html tag will be rendered as - "id[34].propertyName". Number in brackets will be generated - for every iteration and taken from ancestor logic:iterate tag. - ]]> - - - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - maxlength - false - true - - - - - - name - false - true - - - - - - onblur - false - true - - - - - - onchange - false - true - - - - - - onclick - false - true - - - - - - ondblclick - false - true - - - - - - onfocus - false - true - - - - - - onkeydown - false - true - - - - - - onkeypress - false - true - - - - - - onkeyup - false - true - - - - - - onmousedown - false - true - - - - - - onmousemove - false - true - - - - - - onmouseout - false - true - - - - - - onmouseover - false - true - - - - - - onmouseup - false - true - - - - - - property - true - true - - - - - - size - false - true - - - - - - style - false - true - - CSS styles to be applied to this HTML element.

    - -

    N.B. If present, the errorStyle - overrides this attribute in the event of an error for the element.

    - ]]> -
    -
    - - styleClass - false - true - - CSS stylesheet class to be applied to this HTML element - (renders a "class" attribute).

    - -

    N.B. If present, the errorStyleClass - overrides this attribute in the event of an error for the element.

    - ]]> -
    -
    - - styleId - false - true - - Identifier to be assigned to this HTML element (renders - an "id" attribute).

    - -

    N.B. If present, the errorStyleId - overrides this attribute in the event of an error for the element.

    - ]]> -
    -
    - - tabindex - false - true - - - - - - title - false - true - - The advisory title for this element.

    - ]]> -
    -
    - - titleKey - false - true - - The message resources key for the advisory title - for this element.

    - ]]> -
    -
    - - value - false - true - - - NOTE: When setting this to some value, whether - intentional or as the result (for example) of validation errors - forcing the user back to the original jsp, this value is ignored - by most browsers (for security reasons). - This means that your users will have to re-select any previously - selected files when submitting the form. Opera web browser will - prompt the user so they have a chance to abort the submit. -

    - Value to which this field should be initialized. [Use the - corresponding bean property value or body content (if any) if - property is not specified] - ]]> -
    -
    -
    - - form - org.apache.struts.taglib.html.FormTag - JSP - - - Define An Input Form -

    - -

    - Renders an HTML <form> element whose contents are described - by the body content of this tag. The form implicitly interacts - with the specified request scope or session scope bean to populate - the input fields with the current property values from the bean. -

    -

    - The form bean is located, and created if necessary, based on the - form bean specification for the associated ActionMapping. -

    - ]]> -
    - - action - false - true - - The URL to which this form will be submitted. This - value is also used to select the ActionMapping we are - assumed to be processing, from which we can identify - the appropriate form bean and scope. If a value is not - provided, the original URI (servletPath) for the request is - used.

    - -

    If you are using extension mapping for selecting the - controller servlet, this value should be equal to the - path attribute of the corresponding - <action> element, optionally - followed by the correct extension suffix.

    - -

    If you are using path mapping to select the - controller servlet, this value should be exactly equal - to the path attribute of the corresponding - <action> element.

    - ]]> -
    -
    - - acceptCharset - false - true - -
    Since:
    -
    Struts 1.2.2
    - ]]> -
    -
    - - - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - disabled - false - true - boolean - - true if the Form's input fields should be - disabled. - -
    Since:
    -
    Struts 1.2.7
    - ]]> -
    -
    - - enctype - false - true - - - - - - focus - false - true - - - - - - focusIndex - false - true - -
    Since:
    -
    Struts 1.1
    - ]]> -
    -
    - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - method - false - true - - - - - - onreset - false - true - - - - - - onsubmit - false - true - - - - - - readonly - false - true - boolean - - true if the Form's input fields should be - read only. - -
    Since:
    -
    Struts 1.2.7
    - ]]> -
    -
    - - scriptLanguage - false - true - boolean - -
    Since:
    -
    Struts 1.2
    - ]]> -
    -
    - - style - false - true - - - - - - styleClass - false - true - - - - - - styleId - false - true - - - - - - target - false - true - - - - -
    - - frame - org.apache.struts.taglib.html.FrameTag - JSP - - Render an HTML frame element

    - - -

    Renders an HTML <frame> element - with processing for the src attribute that is - identical to that performed by the <html:link> - tag for the href attribute. URL rewriting will be - applied automatically, to maintain session state in the - absence of cookies.

    - -

    The base URL for this frame is calculated based on - which of the following attributes you specify (you must - specify exactly one of them):

    -
      -
    • forward - Use the value of this attribute as the - name of a global ActionForward to be looked - up, and use the module-relative or context-relative - URI found there.
    • -
    • href - Use the value of this attribute unchanged. -
    • -
    • page - Use the value of this attribute as a - module-relative URI, and generate a server-relative - URI by including the context path and application - prefix.
    • -
    • action - Use the value of this attribute as the - logical name of a global Action that contains the actual - content-relative URI of the destination of this transfer.
    • -
    - -

    Normally, the hyperlink you specify with one of the - attributes described in the previous paragraph will be left - unchanged (other than URL rewriting if necessary). However, - there are two ways you can append one or more dynamically - defined query parameters to the hyperlink -- specify a single - parameter with the paramId attribute (and its - associated attributes to select the value), or specify the - name (and optional property) - attributes to select a java.util.Map bean that - contains one or more parameter ids and corresponding values. -

    - -

    To specify a single parameter, use the paramId - attribute to define the name of the request parameter to be - submitted. To specify the corresponding value, use one of the - following approaches:

    -
      -
    • Specify only the paramName attribute - - The named JSP bean (optionally scoped by the value of the - paramScope attribute) must identify a value - that can be converted to a String.
    • -
    • Specify both the paramName and - paramProperty attributes - The specified - property getter method will be called on the JSP bean - identified by the paramName (and optional - paramScope) attributes, in order to select - a value that can be converted to a String.
    • -
    - -

    If you prefer to specify a java.util.Map that - contains all of the request parameters to be added to the - hyperlink, use one of the following techniques:

    -
      -
    • Specify only the name attribute - - The named JSP bean (optionally scoped by the value of - the scope attribute) must identify a - java.util.Map containing the parameters.
    • -
    • Specify both name and - property attributes - The specified - property getter method will be called on the bean - identified by the name (and optional - scope) attributes, in order to return the - java.util.Map containing the parameters.
    • -
    - -

    As the Map is processed, the keys are assumed - to be the names of query parameters to be appended to the - hyperlink. The value associated with each key must be either - a String or a String array representing the parameter value(s), - or an object whose toString() method will be called. - If a String array is specified, more than one value for the - same query parameter name will be created.

    - -

    Additionally, you can request that the current transaction - control token, if any, be included in the generated hyperlink - by setting the transaction attribute to - true. - You can also request that an anchor ("#xxx") be added to the - end of the URL that is created by any of the above mechanisms, - by using the anchor attribute.

    - - ]]> -
    - - bundle - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - action - false - true - - Logical name of a global Action that - contains the actual content-relative URI of the destination - of this transfer. This hyperlink may be dynamically - modified by the inclusion of query parameters, as described - in the tag description. You must specify - exactly one of the action attribute, the - forward attribute, the - href attribute, - or the page attribute.

    - -

    Additionally, you can specify a module prefix - for linking to other modules.

    - - ]]> -
    -
    - - module - false - true - - Prefix name of a Module that - contains the action mapping for the Action - that is specified by the action attribute. - You must specify an action - attribute for this to have an effect.

    - -

    Note: Use "" to map to the default module.

    - ]]> -
    -
    - - anchor - false - true - - Optional anchor tag ("#xxx") to be added to the generated - hyperlink. Specify this value without any - "#" character.

    - ]]> -
    -
    - - forward - false - true - - Logical name of a global ActionForward that - contains the actual content-relative URI of the destination - of this transfer. This hyperlink may be dynamically - modified by the inclusion of query parameters, as described - in the tag description. You must specify - exactly one of the action attribute, the - forward attribute, the - href attribute, - or the page attribute.

    - ]]> -
    -
    - - frameborder - false - true - - Should a frame border be generated around this frame (1) - or not (0)?

    - ]]> -
    -
    - - frameName - false - true - - Value for the name attribute of the rendered - <frame> element.

    - ]]> -
    -
    - - href - false - true - - The URL to which this hyperlink will transfer control - if activated. This hyperlink may be dynamically modified - by the inclusion of query parameters, as described in the - tag description. You must specify - exactly one of the action attribute, the - forward attribute, the - href attribute, - or the page attribute.

    - ]]> -
    -
    - - longdesc - false - true - - URI of a long description of the frame. This description - should supplement the short description provided by the - title attribute, and may be particularly useful - for non-visual user agents.

    - ]]> -
    -
    - - marginheight - false - true - java.lang.Integer - - The amount of space (in pixels) to be left between the - frame's contents and its top and bottom margins.

    - ]]> -
    -
    - - marginwidth - false - true - java.lang.Integer - - The amount of space (in pixels) to be left between the - frame's contents and its left and right margins.

    - ]]> -
    -
    - - name - false - true - - The name of a JSP bean that contains a Map - representing the query parameters (if property - is not specified), or a JSP bean whose property getter is - called to return a Map (if property - is specified).

    - ]]> -
    -
    - - noresize - false - true - boolean - - Should users be disallowed from resizing the frame? - (true, false).

    - ]]> -
    -
    - - page - false - true - - The module-relative path (beginning with a "/" - character) to which this hyperlink will transfer control - if activated. This hyperlink may be dynamically modified - by the inclusion of query parameters, as described in the - tag description. You must specify exactly - one of the action attribute, the - forward attribute, the - href attribute, - or the page attribute.

    - ]]> -
    -
    - - paramId - false - true - - The name of the request parameter that will be dynamically - added to the generated hyperlink. The corresponding value is - defined by the paramName and (optional) - paramProperty attributes, optionally scoped by - the paramScope attribute

    - ]]> -
    -
    - - paramName - false - true - - The name of a JSP bean that is a String containing the - value for the request parameter named by paramId - (if paramProperty is not specified), or a JSP - bean whose property getter is called to return a String - (if paramProperty is specified). The JSP bean - is constrained to the bean scope specified by the - paramScope property, if it is specified.

    - ]]> -
    -
    - - paramProperty - false - true - - The name of a property of the bean specified by the - paramName attribute, whose return value must - be a String containing the value of the request parameter - (named by the paramId attribute) that will be - dynamically added to this hyperlink.

    - ]]> -
    -
    - - paramScope - false - true - - The scope within which to search for the bean specified - by the paramName attribute. If not specified, - all scopes are searched.

    - ]]> -
    -
    - - property - false - true - - The name of a property of the bean specified by the - name attribute, whose return value must be - a java.util.Map containing the query parameters - to be added to the hyperlink. You must - specify the name attribute if you specify - this attribute.

    - ]]> -
    -
    - - scope - false - true - - The scope within which to search for the bean specified - by the name attribute. If not specified, all - scopes are searched.

    - ]]> -
    -
    - - scrolling - false - true - - Should scroll bars be created unconditionally (yes), - never (no), or only when needed (auto)?

    - ]]> -
    -
    - - style - false - true - - CSS styles to be applied to this element.

    - ]]> -
    -
    - - styleClass - false - true - - - - - - styleId - false - true - - - - - - title - false - true - - The advisory title for this element.

    - ]]> -
    -
    - - titleKey - false - true - - The message resources key for the advisory title - for this element.

    - ]]> -
    -
    - - transaction - false - true - boolean - - If set to true, any current transaction - control token will be included in the generated hyperlink, - so that it will pass an isTokenValid() test - in the receiving Action.

    - ]]> -
    -
    -
    - - hidden - org.apache.struts.taglib.html.HiddenTag - empty - - - Render A Hidden Field -

    - -

    - Renders an HTML <input> element of type hidden, populated - from the specified value or the specified property of the bean - associated with our current form. This tag is only valid when - nested inside a form tag body. -

    - ]]> -
    - - accesskey - false - true - - - - - - alt - false - true - - The alternate text for this element.

    - ]]> -
    -
    - - altKey - false - true - - The message resources key of the alternate text for this - element.

    - ]]> -
    -
    - - bundle - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - disabled - false - true - boolean - - true if this input field should be - disabled. - ]]> - - - - indexed - false - true - boolean - - logic:iterate tag. - If true then name of the html tag will be rendered as - "id[34].propertyName". Number in brackets will be generated - for every iteration and taken from ancestor logic:iterate tag. - ]]> - - - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - name - false - true - - - - - - onblur - false - true - - - - - - onchange - false - true - - - - - - onclick - false - true - - - - - - ondblclick - false - true - - - - - - onfocus - false - true - - - - - - onkeydown - false - true - - - - - - onkeypress - false - true - - - - - - onkeyup - false - true - - - - - - onmousedown - false - true - - - - - - onmousemove - false - true - - - - - - onmouseout - false - true - - - - - - onmouseover - false - true - - - - - - onmouseup - false - true - - - - - - property - true - true - - - - - - style - false - true - - - - - - styleClass - false - true - - - - - - styleId - false - true - - - - - - title - false - true - - The advisory title for this element.

    - ]]> -
    -
    - - titleKey - false - true - - The message resources key for the advisory title - for this element.

    - ]]> -
    -
    - - value - false - true - - - - - - write - false - true - boolean - - - - -
    - - html - org.apache.struts.taglib.html.HtmlTag - JSP - - Render an HTML <html> Element

    - -

    Renders an HTML <html> element with - language attributes extracted from the user's current Locale - object, if there is one.

    - ]]> -
    - - lang - false - true - boolean - - Accept-Language - HTTP header is used. If still not found, the default language for the server - is used. - -
    Since:
    -
    Struts 1.2
    - ]]> -
    -
    - - xhtml - false - true - boolean - - Set to true in order to render - xml:lang and xmlns attributes - on the generated html element. This also - causes all other html tags to render as XHTML 1.0 (the - <html:xhtml/> tag has a similar purpose). -

    - -
    Since:
    -
    Struts 1.1
    - ]]> -
    -
    -
    - - image - org.apache.struts.taglib.html.ImageTag - - - Render an input tag of type "image" -

    - - -

    Renders an HTML <input> tag of type - "image". The base URL for this image is calculated directly - based on the value specified in the src or - page attributes, or indirectly by looking up a - message resource string based on the srcKey or - pageKey attributes. You must - specify exactly one of these attributes.

    - -

    If you would like to obtain the coordinates of the mouse - click that submitted this request, see the information below - on the property attribute.

    - -

    This tag is only valid when nested inside a form tag body.

    - - ]]> -
    - - accesskey - false - true - - The keyboard character used to move focus immediately - to this element.

    - ]]> -
    -
    - - align - false - true - - The alignment option for this image.

    - ]]> -
    -
    - - alt - false - true - - The alternate text for this image.

    - ]]> -
    -
    - - altKey - false - true - - The message resources key of the alternate text for this - image.

    - ]]> -
    -
    - - border - false - true - - The width (in pixels) of the border around this image.

    - ]]> -
    -
    - - bundle - false - true - - The servlet context attribute key for the MessageResources - instance to use. If not specified, defaults to the - application resources configured for our action servlet.

    - ]]> -
    -
    - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - disabled - false - true - boolean - - true if this input field should be - disabled. - ]]> - - - - indexed - false - true - boolean - - true - then name of the html tag will be rendered as - "propertyName[34]". Number in brackets will be generated for - every iteration and taken from ancestor logic:iterate tag. - ]]> - - - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - locale - false - true - - The session attribute key for the Locale used to select - internationalized messages. If not specified, defaults to the - Struts standard value.

    - ]]> -
    -
    - - module - false - true - - Prefix name of a Module that - the page or pageKey - attributes relate to.

    - ]]> -
    -
    - - onblur - false - true - - - - - - onchange - false - true - - - - - - onclick - false - true - - - - - - ondblclick - false - true - - - - - - onfocus - false - true - - - - - - onkeydown - false - true - - - - - - onkeypress - false - true - - - - - - onkeyup - false - true - - - - - - onmousedown - false - true - - - - - - onmousemove - false - true - - - - - - onmouseout - false - true - - - - - - onmouseover - false - true - - - - - - onmouseup - false - true - - - - - - page - false - true - - The module-relative path of the image for this - input tag.

    - ]]> -
    -
    - - pageKey - false - true - - The key of the message resources string specifying the - module-relative path of the image for this input tag.

    - ]]> -
    -
    - - property - false - true - - The property name of this image tag. The parameter names - for the request will appear as "property.x" and "property.y", - the x and y representing the coordinates of the mouse click - for the image. A way of retrieving these values through a - form bean is to define getX(), getY(), setX(), and setY() - methods, and specify your property as a blank string - (property="").

    - ]]> -
    -
    - - src - false - true - - The source URL of the image for this input tag.

    - ]]> -
    -
    - - srcKey - false - true - - The key of the message resources string specifying the - source URL of the image for this input tag.

    - ]]> -
    -
    - - style - false - true - - - - - - styleClass - false - true - - - - - - styleId - false - true - - - - - - tabindex - false - true - - - - - - title - false - true - - The advisory title for this element.

    - ]]> -
    -
    - - titleKey - false - true - - The message resources key for the advisory title - for this element.

    - ]]> -
    -
    - - value - false - true - - The value that will be submitted if this image button - is pressed.

    - ]]> -
    -
    -
    - - img - org.apache.struts.taglib.html.ImgTag - empty - - Render an HTML img tag

    - - -

    Renders an HTML <img> element with - the image at the specified URL. Like the link tag, URL - rewriting will be applied automatically to the value - specified in src, page, or - action to maintain session state - in the absence of cookies. This will allow dynamic - generation of an image where the content displayed for this - image will be taken from the attributes of this tag.

    - -

    The base URL for this image is calculated directly based on - the value specified in src, page, or - action or page, - or indirectly by looking up a message resource string based on - the srcKey or pageKey attributes. - You must specify exactly one of these - attributes.

    - -

    Normally, the src, page, or - action that you specify will be left - unchanged (other than URL rewriting if necessary). However, - there are two ways you can append one or more dynamically - defined query parameters to the src URL -- - specify a single parameter with the paramId - attribute (at its associated attributes to select the value), - or specify the name (and optional - property) attributes to select a - java.util.Map bean that contains one or more - parameter ids and corresponding values.

    - -

    To specify a single parameter, use the paramId - attribute to define the name of the request parameter to be - submitted. To specify the corresponding value, use one of the - following approaches:

    -
      -
    • Specify only the paramName attribute - - The named JSP bean (optionally scoped by the value of the - paramScope attribute) must identify a value - that can be converted to a String.
    • -
    • Specify both the paramName and - paramProperty attributes - The specified - property getter will be called on the JSP bean identified - by the paramName (and optional - paramScope) attributes, in order to select - a value that can be converted to a String.
    • -
    - -

    If you prefer to specify a java.util.Map that - contains all of the request parameters to be added to the - hyperlink, use one of the following techniques:

    -
      -
    • Specify only the name attribute - - The named JSP bean (optionally scoped by the value of - the scope attribute) must identify a - java.util.Map containing the parameters.
    • -
    • Specify both name and - property attributes - The specified - property getter method will be called on the bean - identified by the name (and optional - scope) attributes, in order to return the - java.util.Map containing the parameters.
    • -
    - -

    As the Map is processed, the keys are assumed - to be the names of query parameters to be appended to the - src URL. The value associated with each key - must be either a String or a String array representing the - parameter value(s), or an object whose toString() method - will be called. If a String array is specified, more than - one value for the same query parameter name will be - created.

    - -

    You can specify the alternate text for this image (which - most browsers display as pop-up text block when the user - hovers the mouse over this image) either directly, through - the alt attribute, or indirectly from a message - resources bundle, using the bundle and - altKey attributes.

    - - ]]> -
    - - align - false - true - - Where the image is aligned to. Can be one of the - following attributes:

    -
      -
    • left - left justify, wrapping text on right
    • -
    • right -right justify, wrapping test on left
    • -
    • top - aligns the image with the top of the text on - the same row
    • -
    • middle - aligns the image's vertical center with the - text base line
    • -
    • bottom - aligns the image with the bottom of the - text's base line
    • -
    • texttop - aligns the image's top with that of the - text font on the same line
    • -
    • absmiddle - aligns the image's vertical center with the - absolute center of the text
    • -
    • absbottom - aligns the image with the absolute bottom - of the text font on the same row
    • -
    - ]]> -
    -
    - - alt - false - true - - And alternative text to be displayed in browsers that - don't support graphics. Also used often as type of - context help over images.

    - ]]> -
    -
    - - altKey - false - true - - The message resources key of the alternate text for this - element.

    - ]]> -
    -
    - - border - false - true - - The width of the border surrounding the image.

    - ]]> -
    -
    - - bundle - false - true - - The servlet context attribute key for the MessageResources - instance to use. If not specified, defaults to the - application resources configured for our action servlet.

    - ]]> -
    -
    - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - height - false - true - - The height of the image being displayed. This parameter - is very nice to specify (along with width) - to help the browser render the page faster.

    - ]]> -
    -
    - - hspace - false - true - - The amount of horizontal spacing between the icon and - the text. The text may be in the same paragraph, or - be wrapped around the image.

    - ]]> -
    -
    - - imageName - false - true - - The scriptable name to be defined within this page, so - that you can reference it with intra-page scripts. In other - words, the value specified here will render a "name" element - in the generated image tag.

    - ]]> -
    -
    - - ismap - false - true - - The name of the server-side map that this image belongs - to.

    - ]]> -
    -
    - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - locale - false - true - - The name of the request or session Locale attribute used - to look up internationalized messages.

    - ]]> -
    -
    - - name - false - true - - The name of a JSP bean that contains a Map - representing the query parameters (if property - is not specified), or a JSP bean whose property getter is - called to return a Map (if property - is specified).

    - ]]> -
    -
    - - onclick - false - true - - - - - - ondblclick - false - true - - - - - - onkeydown - false - true - - JavaScript event handler that is executed when - this element receives a key down event.

    - ]]> -
    -
    - - onkeypress - false - true - - JavaScript event handler that is executed when - this element receives a key press event.

    - ]]> -
    -
    - - onkeyup - false - true - - JavaScript event handler that is executed when - this element receives a key up event.

    - ]]> -
    -
    - - onmousedown - false - true - - - - - - onmousemove - false - true - - - - - - onmouseout - false - true - - - - - - onmouseover - false - true - - - - - - onmouseup - false - true - - - - - - paramId - false - true - - The name of the request parameter that will be dynamically - added to the generated src URL. The corresponding value is - defined by the paramName and (optional) - paramProperty attributes, optionally scoped by - the paramScope attribute

    - ]]> -
    -
    - - page - false - true - - The module-relative path, starting with a slash, of - the image to be displayed by this tag. The rendered - URL for this image will automatically prepend the context - path of this web application (in the same manner as the - page attribute on the link tag works), - in addition to any necessary URL rewriting. You - must specify either the page - attribute or the src attribute.

    - ]]> -
    -
    - - pageKey - false - true - - The message key, in the message resources bundle named by - the bundle attribute, of the String to be - used as the module-relative path for this image.

    - ]]> -
    -
    - - action - false - true - - The action, starting with a slash, that will render - the image to be displayed by this tag. The rendered - URL for this image will automatically prepend the context - path of this web application (in the same manner as the - action attribute on the link tag works), - in addition to any necessary URL rewriting. You - must specify the action, - page - attribute or the src attribute.

    - -

    Additionally, you can specify a module prefix - for linking to other modules.

    - - ]]> -
    -
    - - module - false - true - - Prefix name of a Module that - contains the action mapping for the Action - that is specified by the action attribute. - You must specify an action - attribute for this to have an effect.

    - -

    Note: Use "" to map to the default module.

    - ]]> -
    -
    - - paramName - false - true - - The name of a JSP bean that is a String containing the - value for the request parameter named by paramId - (if paramProperty is not specified), or a JSP - bean whose property getter is called to return a String - (if paramProperty is specified). The JSP bean - is constrained to the bean scope specified by the - paramScope property, if it is specified.

    - ]]> -
    -
    - - paramProperty - false - true - - The name of a property of the bean specified by the - paramName attribute, whose return value must - be a String containing the value of the request parameter - (named by the paramId attribute) that will be - dynamically added to this src URL.

    - ]]> -
    -
    - - paramScope - false - true - - The scope within which to search for the bean specified - by the paramName attribute. If not specified, - all scopes are searched.

    - ]]> -
    -
    - - property - false - true - - The name of a property of the bean specified by the - name attribute, whose return value must be - a java.util.Map containing the query parameters - to be added to the src URL. You must - specify the name attribute if you specify - this attribute.

    - ]]> -
    -
    - - scope - false - true - - The scope within which to search for the bean specified - by the name attribute. If not specified, all - scopes are searched.

    - ]]> -
    -
    - - src - false - true - - The URL to which this image will be transferred from - This image may be dynamically modified - by the inclusion of query parameters, as described in the - tag description. This value will be used unmodified (other - than potential URL rewriting) as the value of the "src" - attribute in the rendered tag. You must - specify either the page attribute or the - src attribute.

    - ]]> -
    -
    - - srcKey - false - true - - The message key, in the message resources bundle named by - the bundle attribute, of the String to be - used as the URL of this image.

    - ]]> -
    -
    - - style - false - true - - CSS styles to be applied to this element.

    - ]]> -
    -
    - - styleClass - false - true - - - - - - styleId - false - true - - - - - - title - false - true - - The advisory title for this element.

    - ]]> -
    -
    - - titleKey - false - true - - The message resources key for the advisory title - for this element.

    - ]]> -
    -
    - - useLocalEncoding - false - true - boolean - - If set to true, LocalCharacterEncoding will be - used, that is, the characterEncoding set to the HttpServletResponse, - as prefered character encoding rather than UTF-8, when - URLEncoding is done on parameters of the URL.

    - ]]> -
    -
    - - usemap - false - true - - The name of the map as defined within this page for - mapping hot-spot areas of this image.

    - ]]> -
    -
    - - vspace - false - true - - The amount of vertical spacing between the icon and - the text, above and below.

    - ]]> -
    -
    - - width - false - true - - The width of the image being displayed. This parameter - is very nice to specify (along with height) - to help the browser render the page faster.

    - ]]> -
    -
    -
    - - javascript - org.apache.struts.taglib.html.JavascriptValidatorTag - empty - - - Render JavaScript validation based on the - validation rules loaded by the ValidatorPlugIn. -

    - -

    - Render JavaScript validation based on the - validation rules loaded by the ValidatorPlugIn. - The set of validation rules that should be generated is based - on the formName attribute passed in, which should match - the name attribute of the form element in the xml file. -

    -

    - The dynamicJavascript and staticJavascript attributes - default to true, but if dynamicJavascript is set to true - and staticJavascript is set to false then only - the dynamic JavaScript will be rendered. If dynamicJavascript - is set to false - and staticJavascript is set to true then only - the static JavaScript will be rendered which can then be put in - separate JSP page so the browser can cache the static JavaScript. -

    - ]]> -
    - - cdata - false - true - - - If set to "true" and XHTML has been enabled, the JavaScript will - be wrapped in a CDATA section to prevent XML parsing. The default is - "true" to comply with the W3C's recommendation. -

    - -
    Since:
    -
    Struts 1.1
    - ]]> -
    -
    - - dynamicJavascript - false - true - - - Whether or not to render the dynamic JavaScript. - Defaults to true. -

    - ]]> -
    -
    - - formName - false - true - - - The key (form name) to retrieve a specific - set of validation rules. If "dynamicJavascript" is set - to true and formName is missing or is not - recognized by the ValidatorPlugIn, a - JspException will be thrown. -

    - ]]> -
    -
    - - method - false - true - - - The alternate JavaScript method name to be used - instead of the of the default. The default is - 'validate' concatenated in front of - the key (form name) passed in (ex: validateRegistrationForm). -

    - ]]> -
    -
    - - page - false - true - int - - - The current page of a set of validation rules - if the page attribute for the field element - in the xml file is in use. -

    - ]]> -
    -
    - - scriptLanguage - false - true - boolean - -
    Since:
    -
    Struts 1.2
    - ]]> -
    -
    - - src - false - true - - - The src attribute's value when defining - the html script element. -

    - ]]> -
    -
    - - staticJavascript - false - true - - - Whether or not to render the static JavaScript. - Defaults to true. -

    - ]]> -
    -
    - - htmlComment - false - true - - - Whether or not to enclose the javascript - with HTML comments. This attribute is ignored in XHTML - mode because the script would be deleted by the XML parser. See - the cdata attribute for details on hiding scripts from XML - parsers. - Defaults to true. -

    - ]]> -
    -
    - - bundle - false - true - -
    Since:
    -
    Struts 1.2.7
    - ]]> -
    -
    -
    - - link - org.apache.struts.taglib.html.LinkTag - JSP - - Render an HTML anchor or hyperlink

    - - -

    Renders an HTML <a> element as an - anchor definition (if "linkName" is specified) or as a - hyperlink to the specified URL. URL rewriting will be - applied automatically, to maintain session state in the - absence of cookies. The content displayed for this - hyperlink will be taken from the body of this tag.

    - -

    The base URL for this hyperlink is calculated based on - which of the following attributes you specify (you must - specify exactly one of them):

    -
      -
    • forward - Use the value of this attribute as the - name of a global ActionForward to be looked - up, and use the module-relative or context-relative - URI found there. If the forward is module-relative then - it must point to an action and NOT to a page.
    • -
    • action - Use the value of this attribute as the - name of a Action to be looked - up, and use the module-relative or context-relative - URI found there.
    • -
    • href - Use the value of this attribute unchanged. -
    • -
    • page - Use the value of this attribute as a - module-relative URI, and generate a server-relative - URI by including the context path and module - prefix.
    • -
    - -

    Normally, the hyperlink you specify with one of the - attributes described in the previous paragraph will be left - unchanged (other than URL rewriting if necessary). However, - there are three ways you can append one or more dynamically - defined query parameters to the hyperlink -- specify a single - parameter with the paramId attribute (and its - associated attributes to select the value), or specify the - name (and optional property) - attributes to select a java.util.Map bean that - contains one or more parameter ids and corresponding values, - or nest one or more <html:param> tags in the tag body. -

    - -

    To specify a single parameter, use the paramId - attribute to define the name of the request parameter to be - submitted. To specify the corresponding value, use one of the - following approaches:

    -
      -
    • Specify only the paramName attribute - - The named JSP bean (optionally scoped by the value of the - paramScope attribute) must identify a value - that can be converted to a String.
    • -
    • Specify both the paramName and - paramProperty attributes - The specified - property getter method will be called on the JSP bean - identified by the paramName (and optional - paramScope) attributes, in order to select - a value that can be converted to a String.
    • -
    - -

    If you prefer to specify a java.util.Map that - contains all of the request parameters to be added to the - hyperlink, use one of the following techniques:

    -
      -
    • Specify only the name attribute - - The named JSP bean (optionally scoped by the value of - the scope attribute) must identify a - java.util.Map containing the parameters.
    • -
    • Specify both name and - property attributes - The specified - property getter method will be called on the bean - identified by the name (and optional - scope) attributes, in order to return the - java.util.Map containing the parameters.
    • -
    - -

    As the Map is processed, the keys are assumed - to be the names of query parameters to be appended to the - hyperlink. The value associated with each key must be either - a String or a String array representing the parameter value(s), - or an object whose toString() method will be called. - If a String array is specified, more than one value for the - same query parameter name will be created.

    - -

    Supplmenting these two methods, you can nest one or more - <html:param> tags to dynamically add parameters in a - logic-friendly way (such as executing a for loop that - assigns the name/value pairs at runtime). This method does - not compete with the aforementioned; it will adds its - parameters in addition to whatever parameters are - already specified.

    - -

    Additionally, you can request that the current transaction - control token, if any, be included in the generated hyperlink - by setting the transaction attribute to - true. - You can also request that an anchor ("#xxx") be added to the - end of the URL that is created by any of the above mechanisms, - by using the anchor attribute.

    - - ]]> -
    - - accesskey - false - true - - The keyboard character used to move focus immediately - to this element.

    - ]]> -
    -
    - - action - false - true - - Logical name of a Action that - contains the actual content-relative URI of the destination - of this transfer. This hyperlink may be dynamically - modified by the inclusion of query parameters, as described - in the tag description. You must specify - exactly one of the action attribute, the - forward attribute, the - href attribute, the linkName - attribute, or the page attribute.

    - -

    Additionally, you can specify a module prefix - for linking to other modules.

    - - ]]> -
    -
    - - module - false - true - - Prefix name of a Module that - contains the action mapping for the Action - that is specified by the action attribute. - You must specify an action - attribute for this to have an effect.

    - -

    Note: Use "" to map to the default module.

    - ]]> -
    -
    - - anchor - false - true - - Optional anchor tag ("#xxx") to be added to the generated - hyperlink. Specify this value without any - "#" character.

    - ]]> -
    -
    - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - forward - false - true - - Logical name of a global ActionForward that - contains the actual content-relative URI of the destination - of this transfer. This hyperlink may be dynamically - modified by the inclusion of query parameters, as described - in the tag description. You must specify - exactly one of the action attribute, the - forward attribute, the - href attribute, the linkName - attribute, or the page attribute.

    - ]]> -
    -
    - - href - false - true - - The URL to which this hyperlink will transfer control - if activated. This hyperlink may be dynamically modified - by the inclusion of query parameters, as described in the - tag description. You must specify - exactly one of the action attribute, the - forward attribute, the - href attribute, the linkName - attribute, or the page attribute.

    - ]]> -
    -
    - - indexed - false - true - boolean - - true then indexed parameter with name from indexId attribute - will be added to the query string. Indexed parameter looks like - "index[32]". Number in brackets will be generated - for every iteration and taken from ancestor logic:iterate tag. - ]]> - - - - indexId - false - true - - - - - - bundle - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - linkName - false - true - - The anchor name to be defined within this page, so that - you can reference it with intra-page hyperlinks. In other - words, the value specified here will render a "name" element - in the generated anchor tag.

    - ]]> -
    -
    - - name - false - true - - The name of a JSP bean that contains a Map - representing the query parameters (if property - is not specified), or a JSP bean whose property getter is - called to return a Map (if property - is specified).

    - ]]> -
    -
    - - onblur - false - true - - JavaScript event handler that is executed when - this element loses input focus.

    - ]]> -
    -
    - - onclick - false - true - - JavaScript event handler that is executed when - this element receives a mouse click.

    - ]]> -
    -
    - - ondblclick - false - true - - JavaScript event handler that is executed when - this element receives a mouse double click.

    - ]]> -
    -
    - - onfocus - false - true - - JavaScript event handler that is executed when - this element receives input focus.

    - ]]> -
    -
    - - onkeydown - false - true - - JavaScript event handler that is executed when - this element receives a key down event.

    - ]]> -
    -
    - - onkeypress - false - true - - JavaScript event handler that is executed when - this element receives a key press event.

    - ]]> -
    -
    - - onkeyup - false - true - - JavaScript event handler that is executed when - this element receives a key up event.

    - ]]> -
    -
    - - onmousedown - false - true - - JavaScript event handler that is executed when - this element receives a mouse down event.

    - ]]> -
    -
    - - onmousemove - false - true - - JavaScript event handler that is executed when - this element receives a mouse move event.

    - ]]> -
    -
    - - onmouseout - false - true - - JavaScript event handler that is executed when - this element receives a mouse out event.

    - ]]> -
    -
    - - onmouseover - false - true - - JavaScript event handler that is executed when - this element receives a mouse over event.

    - ]]> -
    -
    - - onmouseup - false - true - - JavaScript event handler that is executed when - this element receives a mouse up event.

    - ]]> -
    -
    - - page - false - true - - The module-relative path (beginning with a "/" - character) to which this hyperlink will transfer control - if activated. This hyperlink may be dynamically modified - by the inclusion of query parameters, as described in the - tag description. You must specify exactly - one of the action attribute, - forward attribute, the - href attribute, the linkName - attribute, or the page attribute.

    - ]]> -
    -
    - - paramId - false - true - - The name of the request parameter that will be dynamically - added to the generated hyperlink. The corresponding value is - defined by the paramName and (optional) - paramProperty attributes, optionally scoped by - the paramScope attribute

    - ]]> -
    -
    - - paramName - false - true - - The name of a JSP bean that is a String containing the - value for the request parameter named by paramId - (if paramProperty is not specified), or a JSP - bean whose property getter is called to return a String - (if paramProperty is specified). The JSP bean - is constrained to the bean scope specified by the - paramScope property, if it is specified.

    - ]]> -
    -
    - - paramProperty - false - true - - The name of a property of the bean specified by the - paramName attribute, whose return value must - be a String containing the value of the request parameter - (named by the paramId attribute) that will be - dynamically added to this hyperlink.

    - ]]> -
    -
    - - paramScope - false - true - - The scope within which to search for the bean specified - by the paramName attribute. If not specified, - all scopes are searched.

    - ]]> -
    -
    - - property - false - true - - The name of a property of the bean specified by the - name attribute, whose return value must be - a java.util.Map containing the query parameters - to be added to the hyperlink. You must - specify the name attribute if you specify - this attribute.

    - ]]> -
    -
    - - scope - false - true - - The scope within which to search for the bean specified - by the name attribute. If not specified, all - scopes are searched.

    - ]]> -
    -
    - - style - false - true - - CSS styles to be applied to this element.

    - ]]> -
    -
    - - styleClass - false - true - - - - - - styleId - false - true - - - - - - tabindex - false - true - - The tab order (ascending positive integers) for - this element.

    - ]]> -
    -
    - - target - false - true - - The window target in which the resource requested by this - hyperlink will be displayed, for example in a framed - presentation.

    - ]]> -
    -
    - - title - false - true - - The advisory title for this hyperlink.

    - ]]> -
    -
    - - titleKey - false - true - - The message resources key for the advisory title - for this element.

    - ]]> -
    -
    - - transaction - false - true - boolean - - If set to true, any current transaction - control token will be included in the generated hyperlink, - so that it will pass an isTokenValid() test - in the receiving Action.

    - ]]> -
    -
    - - useLocalEncoding - false - true - boolean - - If set to true, LocalCharacterEncoding will be - used, that is, the characterEncoding set to the HttpServletResponse, - as prefered character encoding rather than UTF-8, when - URLEncoding is done on parameters of the URL.

    - ]]> -
    -
    -
    - - param - org.apache.struts.taglib.html.ParamTag - - Adds a parameter to the following tags: -
      -
    1. <html:frame>
    2. -
    3. <html:link>
    4. -
    5. <html:rewrite>
    6. -
    -

    - -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    - - name - true - true - - The String containing the name of the request parameter.

    - ]]> -
    -
    - - value - false - true - - The value of the request parameter specified by the - name attribute, whose return value must - be a String or String[] that will be dynamically added to - this hyperlink.

    - ]]> -
    -
    -
    - - messages - org.apache.struts.taglib.html.MessagesTag - org.apache.struts.taglib.html.MessagesTei - JSP - - - Conditionally display a set of accumulated messages. -

    - -

    Displays a set of messages prepared by a business - logic component and stored as an ActionMessages - object, ActionErrors object, a String, - or a String array in any scope. If - such a bean is not found, nothing will be rendered. The messages are - placed into the page scope in the body of this tag where they can be displayed - by standard JSP methods. (For example: <bean:write>,<c:out>) -

    - -

    In order to use this tag successfully, you must have - defined an application scope MessageResources - bean under the default attribute name.

    - ]]> -
    - - id - true - false - - null. - ]]> - - - - bundle - false - true - - - - - - locale - false - true - - - - - - name - false - true - - Globals.ERROR_KEY constant string will be used. - ]]> - - - - property - false - true - - - - - - header - false - true - - - - - - footer - false - true - - - - - - message - false - true - - Globals.ERROR_KEY constant string, - but if this attribute is set to 'true' the bean - will be retrieved from the Globals.MESSAGE_KEY - constant string. Also if this is set to 'true', any value - assigned to the name attribute will be ignored. - ]]> - - -
    - - multibox - org.apache.struts.taglib.html.MultiboxTag - - - Render A Checkbox Input Field -

    - -

    Renders an HTML <input> element of type - checkbox, whose "checked" status is - initialized based on whether the specified value - matches one of the elements of the underlying - property's array of current values. This element is - useful when you have large numbers of checkboxes, and - prefer to combine the values into a single - array-valued property instead of multiple boolean - properties. This tag is only valid when nested - inside a form tag body.

    - -

    WARNING: In order to correctly - recognize cases where none of the associated checkboxes - are selected, the ActionForm bean - associated with this form must include a statement - setting the corresponding array to zero length in the - reset() method.

    - -

    The value to be returned to the server, if this checkbox is - selected, must be defined by one of the following methods:

    -
      -
    • Specify a value attribute, whose contents will - be used literally as the value to be returned.
    • -
    • Specify no value attribute, and the nested - body content of this tag will be used as the value to be - returned.
    • -
    - -

    - Also note that a map backed attribute cannot be used to hold a the String[] - for a group of multibox tags. -

    - - ]]> -
    - - accesskey - false - true - - - - - - alt - false - true - - The alternate text for this element.

    - ]]> -
    -
    - - altKey - false - true - - The message resources key of the alternate text for this - element.

    - ]]> -
    -
    - - bundle - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - disabled - false - true - boolean - - true if this input field should be - disabled. - ]]> - - - - errorKey - false - true - - Name of the bean (in any scope) under which our error messages - have been stored. If not present, the name specified by the - Globals.ERROR_KEY constant string will be used.

    - -

    N.B. This is used in conjunction with the - errorStyle, errorStyleClass and - errorStyleId attributes and should be set to - the same value as the name attribute on the - <html:errors/> tag.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyle - false - true - - CSS styles to be applied to this HTML element if - an error exists for it.

    - -

    N.B. If present, this overrides the - style attribute in the event of an error.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleClass - false - true - - CSS stylesheet class to be applied to this HTML element if - an error exists for it (renders a "class" attribute).

    - -

    N.B. If present, this overrides the - styleClass attribute in the event of an error.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleId - false - true - - Identifier to be assigned to this HTML element if - an error exists for it (renders an "id" attribute).

    - -

    N.B. If present, this overrides the - styleId attribute in the event of an error.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - name - false - true - - - - - - onblur - false - true - - - - - - onchange - false - true - - - - - - onclick - false - true - - - - - - ondblclick - false - true - - - - - - onfocus - false - true - - - - - - onkeydown - false - true - - - - - - onkeypress - false - true - - - - - - onkeyup - false - true - - - - - - onmousedown - false - true - - - - - - onmousemove - false - true - - - - - - onmouseout - false - true - - - - - - onmouseover - false - true - - - - - - onmouseup - false - true - - - - - - property - true - true - - - - - - style - false - true - - CSS styles to be applied to this HTML element.

    - -

    N.B. If present, the errorStyle - overrides this attribute in the event of an error for the element.

    - ]]> -
    -
    - - styleClass - false - true - - CSS stylesheet class to be applied to this HTML element - (renders a "class" attribute).

    - -

    N.B. If present, the errorStyleClass - overrides this attribute in the event of an error for the element.

    - ]]> -
    -
    - - styleId - false - true - - Identifier to be assigned to this HTML element (renders - an "id" attribute).

    - -

    N.B. If present, the errorStyleId - overrides this attribute in the event of an error for the element.

    - ]]> -
    -
    - - tabindex - false - true - - - - - - title - false - true - - The advisory title for this element.

    - ]]> -
    -
    - - titleKey - false - true - - The message resources key for the advisory title - for this element.

    - ]]> -
    -
    - - value - false - true - - - - -
    - - option - org.apache.struts.taglib.html.OptionTag - - - Render A Select Option -

    - -

    Render an HTML <option> element, - representing one of the choices for an enclosing - <select> element. The text displayed to the - user comes from either the body of this tag, or from a message - string looked up based on the bundle, - locale, and key attributes.

    - -

    If the value of the corresponding bean property matches the - specified value, this option will be marked selected. This tag - is only valid when nested inside a - <html:select> tag body.

    - ]]> -
    - - bundle - false - true - - - - - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - disabled - false - true - boolean - - true if this option should be - disabled. - ]]> - - - - filter - false - true - boolean - - true if you want the option label to be - filtered for sensitive characters in HTML. By default, such - a value is NOT filtered. - ]]> - - - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - key - false - true - - bundle for - the text displayed to the user for this option. If not - specified, the text to be displayed is taken from the body - content of this tag. - ]]> - - - - locale - false - true - - key attribute. If not specified, uses the - standard Struts session attribute name. - ]]> - - - - style - false - true - - - - - - styleId - false - true - - - - - - styleClass - false - true - - - - - - value - true - true - - - - -
    - - options - org.apache.struts.taglib.html.OptionsTag - empty - - - Render a Collection of Select Options -

    - -

    Renders a set of HTML <option> elements, - representing possible choices for a <select> - element. This tag can be used multiple times within a single - <html:select> element, either in conjunction - with or instead of one or more <html:option> - or <html:optionsCollection> elements.

    - -

    This tag operates in one of two major modes, depending on - whether or not the collection attribute is - specified. If the collection attribute is - included, the following rules apply:

    -
      -
    • The collection attribute is interpreted - as the name of a JSP bean, in some scope, that itself - represents a collection of individual beans, one per option - value to be rendered.
    • -
    • The property attribute is interpreted as - the name of a property of the individual beans included in - the collection, and is used to retrieve the value that will - be returned to the server if this option is selected.
    • -
    • The labelProperty attribute is interpreted - as the name of a property of the individual beans included - in the collection, and is used to retrieve the label that - will be displayed to the user for this option. If the - labelProperty attribute is not specified, the - property named by the property attribute will - be used to select both the value returned to the server and - the label displayed to the user for this option.
    • -
    - -

    If the collection attribute is not specified, - the rules described in the remainder of this section apply.

    - -

    The collection of values actually selected depends on the presence or - absence of the name and property attributes. The - following combinations are allowed:

    -
      -
    • Only name is specified - The value of this attribute - is the name of a JSP bean in some scope that is the - collection.
    • -
    • Only property is specified - The value of this - attribute is the name of a property of the ActionForm bean associated - with our form, which will return the collection.
    • -
    • Both name and property are specified - - The value of the name attribute identifies a JSP bean - in some scope. The value of the property attribute is the - name of some property of that bean which will return the collection.
    • -
    - -

    The collection of labels displayed to the user can be the same as the - option values themselves, or can be different, depending on the presence or - absence of the labelName and labelProperty - attributes. If this feature is used, the collection of labels must contain - the same number of elements as the corresponding collection of values. - The following combinations are allowed:

    -
      -
    • Neither labelName nor labelProperty is - specified - The labels will be the same as the option values - themselves.
    • -
    • Only labelName is specified - The value of this - attribute is the name of a JSP bean in some scope that is the - collection.
    • -
    • Only labelProperty is specified - The value of this - attribute is the name of a property of the ActionForm bean associated - with our form, which will return the collection.
    • -
    • Both labelName and labelProperty are - specified - The value of the labelName attribute - identifies a JSP bean in some scope. The value of the - labelProperty attribute is the name of some property of - that bean which will return the collection.
    • -
    - - -

    Note that this tag does not support a styleId - attribute, as it would have to apply the value to all the - option elements created by this element, which would - mean that more than one id element might have the same - value, which the HTML specification says is illegal.

    - - ]]> -
    - - collection - false - true - - - - - - filter - false - true - boolean - - false if you do NOT want the option labels - filtered for sensitive characters in HTML. By default, such - values are filtered. - ]]> - - - - labelName - false - true - - - - - - labelProperty - false - true - - - - - - name - false - true - - - - - - property - false - true - - - - - - style - false - true - - - - - - styleClass - false - true - - - - -
    - - optionsCollection - org.apache.struts.taglib.html.OptionsCollectionTag - empty - - - Render a Collection of Select Options -

    - -

    Renders a set of HTML <option> elements, - representing possible choices for a <select> - element. This tag can be used multiple times within a single - <html:select> element, either in conjunction - with or instead of one or more <html:option> - or <html:options> elements.

    - -

    This tag operates on a collection of beans, where each bean - has a label property and a value - property. The actual names of these properties can be configured - using the label and value attributes - of this tag.

    - -

    This tag differs from the <html:options> tag - in that it makes more consistent use of the name and - property attributes, and allows the collection to be - more easily obtained from the enclosing form bean.

    - -

    Note that this tag does not support a styleId - attribute, as it would have to apply the value to all the - option elements created by this element, which would - mean that more than one id element might have the same - value, which the HTML specification says is illegal.

    - ]]> -
    - - filter - false - true - boolean - - false if you do NOT want the option labels - filtered for sensitive characters in HTML. By default, such - values are filtered. - ]]> - - - - label - false - true - - - - - - name - false - true - - - - - - property - false - true - - - - - - style - false - true - - - - - - styleClass - false - true - - - - - - value - false - true - - - - -
    - - password - org.apache.struts.taglib.html.PasswordTag - - - Render A Password Input Field -

    - - Renders an HTML <input> element of type password, populated - from the specified value or the specified property of the bean - associated with our current form. This tag is only valid when - nested inside a form tag body. - ]]> -
    - - accesskey - false - true - - - - - - alt - false - true - - The alternate text for this element.

    - ]]> -
    -
    - - altKey - false - true - - The message resources key of the alternate text for this - element.

    - ]]> -
    -
    - - - - bundle - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - disabled - false - true - boolean - - true if this input field should be - disabled. - ]]> - - - - errorKey - false - true - - Name of the bean (in any scope) under which our error messages - have been stored. If not present, the name specified by the - Globals.ERROR_KEY constant string will be used.

    - -

    N.B. This is used in conjunction with the - errorStyle, errorStyleClass and - errorStyleId attributes and should be set to - the same value as the name attribute on the - <html:errors/> tag.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyle - false - true - - CSS styles to be applied to this HTML element if - an error exists for it.

    - -

    N.B. If present, this overrides the - style attribute in the event of an error.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleClass - false - true - - CSS stylesheet class to be applied to this HTML element if - an error exists for it (renders a "class" attribute).

    - -

    N.B. If present, this overrides the - styleClass attribute in the event of an error.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleId - false - true - - Identifier to be assigned to this HTML element if - an error exists for it (renders an "id" attribute).

    - -

    N.B. If present, this overrides the - styleId attribute in the event of an error.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - indexed - false - true - boolean - - true then name of the html tag will be rendered as - "id[34].propertyName". Number in brackets will be generated - for every iteration and taken from ancestor logic:iterate tag. - ]]> - - - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - maxlength - false - true - - - - - - name - false - true - - - - - - onblur - false - true - - - - - - onchange - false - true - - - - - - onclick - false - true - - - - - - ondblclick - false - true - - - - - - onfocus - false - true - - - - - - onkeydown - false - true - - - - - - onkeypress - false - true - - - - - - onkeyup - false - true - - - - - - onmousedown - false - true - - - - - - onmousemove - false - true - - - - - - onmouseout - false - true - - - - - - onmouseover - false - true - - - - - - onmouseup - false - true - - - - - - onselect - false - true - -
    Since:
    -
    Struts 1.3.10
    - ]]> -
    -
    - - property - true - true - - - - - - readonly - false - true - boolean - - true if this input field should be - read only. - ]]> - - - - redisplay - false - true - boolean - - false on login pages. - Defaults to true for consistency with - all other form tags that redisplay their contents. - ]]> - - - - style - false - true - - CSS styles to be applied to this HTML element.

    - -

    N.B. If present, the errorStyle - overrides this attribute in the event of an error for the element.

    - ]]> -
    -
    - - styleClass - false - true - - CSS stylesheet class to be applied to this HTML element - (renders a "class" attribute).

    - -

    N.B. If present, the errorStyleClass - overrides this attribute in the event of an error for the element.

    - ]]> -
    -
    - - styleId - false - true - - Identifier to be assigned to this HTML element (renders - an "id" attribute).

    - -

    N.B. If present, the errorStyleId - overrides this attribute in the event of an error for the element.

    - ]]> -
    -
    - - size - false - true - - - - - - tabindex - false - true - - - - - - title - false - true - - The advisory title for this element.

    - ]]> -
    -
    - - titleKey - false - true - - The message resources key for the advisory title - for this element.

    - ]]> -
    -
    - - value - false - true - - - - -
    - - radio - org.apache.struts.taglib.html.RadioTag - - - Render A Radio Button Input Field -

    - -

    - Renders an HTML <input> element of type radio, populated from - the specified property of the bean associated with our current form. - This tag is only valid when nested inside a form tag body. -

    -

    - If an iterator is used to render a series of radio tags, the - idName attribute may be used to specify the name of the bean - exposed by the iterator. In this case, the value attribute is - used as the name of a property on the idName bean that returns - the value of the radio tag in this iteration. -

    - ]]> -
    - - accesskey - false - true - - - - - - alt - false - true - - The alternate text for this element.

    - ]]> -
    -
    - - altKey - false - true - - The message resources key of the alternate text for this - element.

    - ]]> -
    -
    - - bundle - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - disabled - false - true - boolean - - true if this input field should be - disabled. - ]]> - - - - errorKey - false - true - - Name of the bean (in any scope) under which our error messages - have been stored. If not present, the name specified by the - Globals.ERROR_KEY constant string will be used.

    - -

    N.B. This is used in conjunction with the - errorStyle, errorStyleClass and - errorStyleId attributes and should be set to - the same value as the name attribute on the - <html:errors/> tag.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyle - false - true - - CSS styles to be applied to this HTML element if - an error exists for it.

    - -

    N.B. If present, this overrides the - style attribute in the event of an error.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleClass - false - true - - CSS stylesheet class to be applied to this HTML element if - an error exists for it (renders a "class" attribute).

    - -

    N.B. If present, this overrides the - styleClass attribute in the event of an error.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleId - false - true - - Identifier to be assigned to this HTML element if - an error exists for it (renders an "id" attribute).

    - -

    N.B. If present, this overrides the - styleId attribute in the event of an error.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - indexed - false - true - boolean - - true then name of the html tag will be rendered as - "id[34].propertyName". Number in brackets will be generated - for every iteration and taken from ancestor logic:iterate tag. - ]]> - - - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - name - false - true - - - - - - onblur - false - true - - - - - - onchange - false - true - - - - - - onclick - false - true - - - - - - ondblclick - false - true - - - - - - onfocus - false - true - - - - - - onkeydown - false - true - - - - - - onkeypress - false - true - - - - - - onkeyup - false - true - - - - - - property - true - true - - - - - - onmousedown - false - true - - - - - - onmousemove - false - true - - - - - - onmouseout - false - true - - - - - - onmouseover - false - true - - - - - - onmouseup - false - true - - - - - - style - false - true - - CSS styles to be applied to this HTML element.

    - -

    N.B. If present, the errorStyle - overrides this attribute in the event of an error for the element.

    - ]]> -
    -
    - - styleClass - false - true - - CSS stylesheet class to be applied to this HTML element - (renders a "class" attribute).

    - -

    N.B. If present, the errorStyleClass - overrides this attribute in the event of an error for the element.

    - ]]> -
    -
    - - styleId - false - true - - Identifier to be assigned to this HTML element (renders - an "id" attribute).

    - -

    N.B. If present, the errorStyleId - overrides this attribute in the event of an error for the element.

    - ]]> -
    -
    - - tabindex - false - true - - - - - - title - false - true - - The advisory title for this element.

    - ]]> -
    -
    - - titleKey - false - true - - The message resources key for the advisory title - for this element.

    - ]]> -
    -
    - - value - true - true - - - - - - idName - false - true - - Name of the bean (in some scope) that will return the - value of the radio tag. Usually exposed - by an iterator. When the idName attribute is - present, the value attribute is used as the name of the - property on the idName bean that will return the - value of the radio tag for this iteration.

    - -
    Since:
    -
    Struts 1.1
    - ]]> -
    -
    -
    - - reset - org.apache.struts.taglib.html.ResetTag - - - Render A Reset Button Input Field -

    - - Renders an HTML <input> element of type reset. - ]]> -
    - - accesskey - false - true - - - - - - alt - false - true - - The alternate text for this element.

    - ]]> -
    -
    - - altKey - false - true - - The message resources key of the alternate text for this - element.

    - ]]> -
    -
    - - bundle - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - disabled - false - true - boolean - - true if this input field should be - disabled. - ]]> - - - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - onblur - false - true - - - - - - onchange - false - true - - - - - - onclick - false - true - - - - - - ondblclick - false - true - - - - - - onfocus - false - true - - - - - - onkeydown - false - true - - - - - - onkeypress - false - true - - - - - - onkeyup - false - true - - - - - - onmousedown - false - true - - - - - - onmousemove - false - true - - - - - - onmouseout - false - true - - - - - - onmouseover - false - true - - - - - - onmouseup - false - true - - - - - - property - false - true - - - - - - style - false - true - - - - - - styleClass - false - true - - - - - - styleId - false - true - - - - - - tabindex - false - true - - - - - - title - false - true - - The advisory title for this element.

    - ]]> -
    -
    - - titleKey - false - true - - The message resources key for the advisory title - for this element.

    - ]]> -
    -
    - - value - false - true - - - - -
    - - rewrite - org.apache.struts.taglib.html.RewriteTag - JSP - - Render an URI

    - -

    Renders a request URI based on exactly the same rules - as the <html:link> tag does, - but without creating - the <a> hyperlink. This value is useful - when you want to generate a string constant for use by - a JavaScript procedure.

    - ]]> -
    - - action - false - true - - Logical name of a Action that - contains the actual content-relative URI of the destination - of this transfer. This hyperlink may be dynamically - modified by the inclusion of query parameters, as described - in the tag description. You must specify - exactly one of the action attribute, the - forward attribute, the - href attribute, or the page - attribute.

    - -

    Additionally, you can specify a module prefix - for linking to other modules.

    - - -
    Since:
    -
    Struts 1.2.0
    - ]]> -
    -
    - - module - false - true - - Prefix name of a Module that - contains the action mapping for the Action - that is specified by the action attribute. - You must specify an action - attribute for this to have an effect.

    - -

    Note: Use "" to map to the default module.

    - ]]> -
    -
    - - anchor - false - true - - Optional anchor tag ("#xxx") to be added to the generated - hyperlink. Specify this value without any - "#" character.

    - ]]> -
    -
    - - forward - false - true - - Logical name of a global ActionForward that - contains the actual content-relative URI of the destination - of this transfer. This hyperlink may be dynamically - modified by the inclusion of query parameters, as described - in the tag description. You must specify - exactly one of the action attribute, the - forward attribute, the - href attribute, or the page - attribute.

    - ]]> -
    -
    - - href - false - true - - The URL to which this hyperlink will transfer control - if activated. This hyperlink may be dynamically modified - by the inclusion of query parameters, as described in the - tag description. You must specify - exactly one of the action attribute, the - forward attribute, the - href attribute, or the page - attribute.

    - ]]> -
    -
    - - name - false - true - - The name of a JSP bean that contains a Map - representing the query parameters (if property - is not specified), or a JSP bean whose property getter is - called to return a Map (if property - is specified).

    - ]]> -
    -
    - - page - false - true - - The module-relative path (beginning with a "/" - character) to which this hyperlink will transfer control - if activated. This hyperlink may be dynamically modified - by the inclusion of query parameters, as described in the - tag description. You must specify exactly - one of the action attribute, the - forward attribute, the - href attribute, or the page - attribute.

    - ]]> -
    -
    - - paramId - false - true - - The name of the request parameter that will be dynamically - added to the generated hyperlink. The corresponding value is - defined by the paramName and (optional) - paramProperty attributes, optionally scoped by - the paramScope attribute

    - ]]> -
    -
    - - paramName - false - true - - The name of a JSP bean that is a String containing the - value for the request parameter named by paramId - (if paramProperty is not specified), or a JSP - bean whose property getter is called to return a String - (if paramProperty is specified). The JSP bean - is constrained to the bean scope specified by the - paramScope property, if it is specified.

    - ]]> -
    -
    - - paramProperty - false - true - - The name of a property of the bean specified by the - paramName attribute, whose return value must - be a String containing the value of the request parameter - (named by the paramId attribute) that will be - dynamically added to this hyperlink.

    - ]]> -
    -
    - - paramScope - false - true - - The scope within which to search for the bean specified - by the paramName attribute. If not specified, - all scopes are searched.

    - ]]> -
    -
    - - property - false - true - - The name of a property of the bean specified by the - name attribute, whose return value must be - a java.util.Map containing the query parameters - to be added to the hyperlink. You must - specify the name attribute if you specify - this attribute.

    - ]]> -
    -
    - - scope - false - true - - The scope within which to search for the bean specified - by the name attribute. If not specified, all - scopes are searched.

    - ]]> -
    -
    - - transaction - false - true - boolean - - If set to true, any current transaction - control token will be included in the generated hyperlink, - so that it will pass an isTokenValid() test - in the receiving Action.

    - ]]> -
    -
    - - useLocalEncoding - false - true - boolean - - If set to true, LocalCharacterEncoding will be - used, that is, the characterEncoding set to the HttpServletResponse, - as prefered character encoding rather than UTF-8, when - URLEncoding is done on parameters of the URL.

    - ]]> -
    -
    -
    - - select - org.apache.struts.taglib.html.SelectTag - JSP - - - Render A Select Element -

    - -

    Renders an HTML <select> element, associated - with a bean property specified by our attributes. This - tag is only valid when nested inside a form tag body. -

    - -

    This tag operates in two modes, depending upon the - state of the multiple attribute, which - affects the data type of the associated property you - should use:

    -
      -
    • multiple="true" IS NOT selected - - The corresponding property should be a scalar - value of any supported data type.
    • -
    • multiple="true" IS selected - - The corresponding property should be an array - of any supported data type.
    • -
    - -

    WARNING: In order to correctly - recognize cases where no selection at all is made, the - ActionForm bean associated with this form - must include a statement resetting the scalar property - to a default value (if multiple is not - set), or the array property to zero length (if - multiple is set) in the - reset() method.

    - ]]> -
    - - alt - false - true - - The alternate text for this element.

    - ]]> -
    -
    - - altKey - false - true - - The message resources key of the alternate text for this - element.

    - ]]> -
    -
    - - bundle - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - disabled - false - true - boolean - - true if this input field should be - disabled. - ]]> - - - - errorKey - false - true - - Name of the bean (in any scope) under which our error messages - have been stored. If not present, the name specified by the - Globals.ERROR_KEY constant string will be used.

    - -

    N.B. This is used in conjunction with the - errorStyle, errorStyleClass and - errorStyleId attributes and should be set to - the same value as the name attribute on the - <html:errors/> tag.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyle - false - true - - CSS styles to be applied to this HTML element if - an error exists for it.

    - -

    N.B. If present, this overrides the - style attribute in the event of an error.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleClass - false - true - - CSS stylesheet class to be applied to this HTML element if - an error exists for it (renders a "class" attribute).

    - -

    N.B. If present, this overrides the - styleClass attribute in the event of an error.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleId - false - true - - Identifier to be assigned to this HTML element if - an error exists for it (renders an "id" attribute).

    - -

    N.B. If present, this overrides the - styleId attribute in the event of an error.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - indexed - false - true - boolean - - true then name of the html tag will be rendered as - "id[34].propertyName". Number in brackets will be generated - for every iteration and taken from ancestor logic:iterate tag. - ]]> - - - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - multiple - false - true - - - - - - name - false - true - - <html:form> tag is utilized. - ]]> - - - - onblur - false - true - - - - - - onchange - false - true - - - - - - onclick - false - true - - - - - - ondblclick - false - true - - - - - - onfocus - false - true - - - - - - onkeydown - false - true - - - - - - onkeypress - false - true - - - - - - onkeyup - false - true - - - - - - onmousedown - false - true - - - - - - onmousemove - false - true - - - - - - onmouseout - false - true - - - - - - onmouseover - false - true - - - - - - onmouseup - false - true - - - - - - property - true - true - - - - - - style - false - true - - CSS styles to be applied to this HTML element.

    - -

    N.B. If present, the errorStyle - overrides this attribute in the event of an error for the element.

    - ]]> -
    -
    - - styleClass - false - true - - CSS stylesheet class to be applied to this HTML element - (renders a "class" attribute).

    - -

    N.B. If present, the errorStyleClass - overrides this attribute in the event of an error for the element.

    - ]]> -
    -
    - - styleId - false - true - - Identifier to be assigned to this HTML element (renders - an "id" attribute).

    - -

    N.B. If present, the errorStyleId - overrides this attribute in the event of an error for the element.

    - ]]> -
    -
    - - tabindex - false - true - - - - - - size - false - true - - - - - - title - false - true - - The advisory title for this element.

    - ]]> -
    -
    - - titleKey - false - true - - The message resources key for the advisory title - for this element.

    - ]]> -
    -
    - - value - false - true - - - - -
    - - submit - org.apache.struts.taglib.html.SubmitTag - - - Render A Submit Button -

    - - Renders an HTML <input> element of type submit. -

    - If a graphical button is needed (a button with an image), then the - <html:image> tag is more appropriate. -

    - ]]> -
    - - accesskey - false - true - - - - - - alt - false - true - - The alternate text for this element.

    - ]]> -
    -
    - - altKey - false - true - - The message resources key of the alternate text for this - element.

    - ]]> -
    -
    - - bundle - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - disabled - false - true - boolean - - true if this input field should be - disabled. - ]]> - - - - indexed - false - true - boolean - - true - then name of the html tag will be rendered as - "propertyName[34]". Number in brackets will be generated for - every iteration and taken from ancestor logic:iterate tag. - ]]> - - - - onblur - false - true - - - - - - onchange - false - true - - - - - - onclick - false - true - - - - - - ondblclick - false - true - - - - - - onfocus - false - true - - - - - - onkeydown - false - true - - - - - - onkeypress - false - true - - - - - - onkeyup - false - true - - - - - - onmousedown - false - true - - - - - - onmousemove - false - true - - - - - - onmouseout - false - true - - - - - - onmouseover - false - true - - - - - - onmouseup - false - true - - - - - - property - false - true - - - - - - style - false - true - - - - - - styleClass - false - true - - - - - - styleId - false - true - - - - - - tabindex - false - true - - - - - - title - false - true - - The advisory title for this element.

    - ]]> -
    -
    - - titleKey - false - true - - The message resources key for the advisory title - for this element.

    - ]]> -
    -
    - - value - false - true - - - - -
    - - text - org.apache.struts.taglib.html.TextTag - - Render An Input Field of Type text

    - -

    Render an input field of type text. This tag is only valid when - nested inside a form tag body.

    - ]]> -
    - - accesskey - false - true - - The keyboard character used to move focus immediately to this - element.

    - ]]> -
    -
    - - alt - false - true - - The alternate text for this element.

    - ]]> -
    -
    - - altKey - false - true - - The message resources key of the alternate text for this - element.

    - ]]> -
    -
    - - - - bundle - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - disabled - false - true - boolean - - true if this input field should be - disabled. - ]]> - - - - errorKey - false - true - - Name of the bean (in any scope) under which our error messages - have been stored. If not present, the name specified by the - Globals.ERROR_KEY constant string will be used.

    - -

    N.B. This is used in conjunction with the - errorStyle, errorStyleClass and - errorStyleId attributes and should be set to - the same value as the name attribute on the - <html:errors/> tag.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyle - false - true - - CSS styles to be applied to this HTML element if - an error exists for it.

    - -

    N.B. If present, this overrides the - style attribute in the event of an error.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleClass - false - true - - CSS stylesheet class to be applied to this HTML element if - an error exists for it (renders a "class" attribute).

    - -

    N.B. If present, this overrides the - styleClass attribute in the event of an error.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleId - false - true - - Identifier to be assigned to this HTML element if - an error exists for it (renders an "id" attribute).

    - -

    N.B. If present, this overrides the - styleId attribute in the event of an error.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - indexed - false - true - boolean - - true then name of the html tag will be rendered as - "id[34].propertyName". Number in brackets will be generated - for every iteration and taken from ancestor logic:iterate tag. - ]]> - - - - maxlength - false - true - - - - - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - name - false - true - - - - - - onblur - false - true - - - - - - onchange - false - true - - - - - - onclick - false - true - - - - - - ondblclick - false - true - - - - - - onfocus - false - true - - - - - - onkeydown - false - true - - - - - - onkeypress - false - true - - - - - - onkeyup - false - true - - - - - - onmousedown - false - true - - - - - - onmousemove - false - true - - - - - - onmouseout - false - true - - - - - - onmouseover - false - true - - - - - - onmouseup - false - true - - - - - - onselect - false - true - - - - - - property - true - true - - - - - - readonly - false - true - boolean - - true if this input field should be - read only. - ]]> - - - - size - false - true - - - - - - style - false - true - - CSS styles to be applied to this HTML element.

    - -

    N.B. If present, the errorStyle - overrides this attribute in the event of an error for the element.

    - ]]> -
    -
    - - styleClass - false - true - - CSS stylesheet class to be applied to this HTML element - (renders a "class" attribute).

    - -

    N.B. If present, the errorStyleClass - overrides this attribute in the event of an error for the element.

    - ]]> -
    -
    - - styleId - false - true - - Identifier to be assigned to this HTML element (renders - an "id" attribute).

    - -

    N.B. If present, the errorStyleId - overrides this attribute in the event of an error for the element.

    - ]]> -
    -
    - - tabindex - false - true - - - - - - title - false - true - - The advisory title for this element.

    - ]]> -
    -
    - - titleKey - false - true - - The message resources key for the advisory title - for this element.

    - ]]> -
    -
    - - value - false - true - - - - -
    - - textarea - org.apache.struts.taglib.html.TextareaTag - - - Render A Textarea -

    - - Render a textarea element. This tag is only valid when nested - inside a form tag body. - ]]> -
    - - accesskey - false - true - - - - - - alt - false - true - - The alternate text for this element.

    - ]]> -
    -
    - - altKey - false - true - - The message resources key of the alternate text for this - element.

    - ]]> -
    -
    - - bundle - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - cols - false - true - - - - - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - disabled - false - true - boolean - - true if this input field should be - disabled. - ]]> - - - - errorKey - false - true - - Name of the bean (in any scope) under which our error messages - have been stored. If not present, the name specified by the - Globals.ERROR_KEY constant string will be used.

    - -

    N.B. This is used in conjunction with the - errorStyle, errorStyleClass and - errorStyleId attributes and should be set to - the same value as the name attribute on the - <html:errors/> tag.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyle - false - true - - CSS styles to be applied to this HTML element if - an error exists for it.

    - -

    N.B. If present, this overrides the - style attribute in the event of an error.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleClass - false - true - - CSS stylesheet class to be applied to this HTML element if - an error exists for it (renders a "class" attribute).

    - -

    N.B. If present, this overrides the - styleClass attribute in the event of an error.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleId - false - true - - Identifier to be assigned to this HTML element if - an error exists for it (renders an "id" attribute).

    - -

    N.B. If present, this overrides the - styleId attribute in the event of an error.

    - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - indexed - false - true - boolean - - true then name of the html tag will be rendered as - "id[34].propertyName". Number in brackets will be generated - for every iteration and taken from ancestor logic:iterate tag. - ]]> - - - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - name - false - true - - - - - - onblur - false - true - - - - - - onchange - false - true - - - - - - onclick - false - true - - - - - - ondblclick - false - true - - - - - - onfocus - false - true - - - - - - onkeydown - false - true - - - - - - onkeypress - false - true - - - - - - onkeyup - false - true - - - - - - onmousedown - false - true - - - - - - onmousemove - false - true - - - - - - onmouseout - false - true - - - - - - onmouseover - false - true - - - - - - onmouseup - false - true - - - - - - onselect - false - true - - - - - - property - true - true - - - - - - readonly - false - true - boolean - - true if this input field should be - read only. - ]]> - - - - rows - false - true - - - - - - style - false - true - - CSS styles to be applied to this HTML element.

    - -

    N.B. If present, the errorStyle - overrides this attribute in the event of an error for the element.

    - ]]> -
    -
    - - styleClass - false - true - - CSS stylesheet class to be applied to this HTML element - (renders a "class" attribute).

    - -

    N.B. If present, the errorStyleClass - overrides this attribute in the event of an error for the element.

    - ]]> -
    -
    - - styleId - false - true - - Identifier to be assigned to this HTML element (renders - an "id" attribute).

    - -

    N.B. If present, the errorStyleId - overrides this attribute in the event of an error for the element.

    - ]]> -
    -
    - - tabindex - false - true - - - - - - title - false - true - - The advisory title for this element.

    - ]]> -
    -
    - - titleKey - false - true - - The message resources key for the advisory title - for this element.

    - ]]> -
    -
    - - value - false - true - - - - -
    - - xhtml - org.apache.struts.taglib.html.XhtmlTag - empty - - Render HTML tags as XHTML

    - -

    - Using this tag in a page tells all other html taglib tags - to render themselves as XHTML 1.0. This is useful - when composing pages with JSP includes or Tiles. - <html:html xhtml="true"> has a similar effect. This - tag has no attributes; you use it like this: <html:xhtml/>. -

    -

    - Note: Included pages do not inherit the rendering - style of the including page. Each JSP fragment or Tile must use this - tag to render as XHTML. -

    - ]]> -
    -
    -
    - diff --git a/oeqPrimaryB2/src/main/webapp/taglibs/config/struts/struts-logic.tld b/oeqPrimaryB2/src/main/webapp/taglibs/config/struts/struts-logic.tld deleted file mode 100644 index 303ae57..0000000 --- a/oeqPrimaryB2/src/main/webapp/taglibs/config/struts/struts-logic.tld +++ /dev/null @@ -1,1891 +0,0 @@ - - - - - 1.3 - 1.2 - logic - http://struts.apache.org/tags-logic - - Note: Some of the features in this taglib are also - available in the JavaServer Pages Standard Tag Library (JSTL). - The Struts team encourages the use of the standard tags over the Struts - specific tags when possible.

    - -

    This tag library contains tags that are useful in managing conditional - generation of output text, looping over object collections for - repetitive generation of output text, and application flow management.

    - -

    For tags that do value comparisons (equal, - greaterEqual, greaterThan, lessEqual, - lessThan, notEqual), the following rules apply:

    -
      -
    • The specified value is examined. If it can be converted successfully - to a double or a long, it is assumed that the - ultimate comparison will be numeric (either floating point or integer). - Otherwise, a String comparison will be performed.
    • -
    • The variable to be compared to is retrieved, based on the selector - attribute(s) (cookie, header, - name, parameter, property) - present on this tag. It will be converted to the appropriate type - for the comparison, as determined above.
    • -
    • If the specified variable or property returns null, it will be - coerced to a zero-length string before the comparison occurs.
    • -
    • The specific comparison for this tag will be performed, and the nested - body content of this tag will be evaluated if the comparison returns - a true result.
    • -
    - -

    For tags that do substring matching (match, - notMatch), the following rules apply:

    -
      -
    • The specified variable is retrieved, based on the selector attribute(s) - (cookie, header, name, - parameter, property) present on this tag. - The variable is converted to a String, if necessary.
    • -
    • A request time exception will be thrown if the specified variable - cannot be retrieved, or has a null value.
    • -
    • The specified value is checked for existence as a substring of the - variable, in the position specified by the location - attribute, as follows: at the beginning (if location is set to - start), at the end (if location is set to - end), or anywhere (if location is not specified).
    • -
    - -

    Many of the tags in this tag library will throw a - JspException at runtime when they are utilized incorrectly - (such as when you specify an invalid combination of tag attributes). JSP - allows you to declare an "error page" in the <%@ page %> - directive. If you wish to process the actual exception that caused the - problem, it is passed to the error page as a request attribute under key - org.apache.struts.action.EXCEPTION.

    - - ]]> -
    - - empty - org.apache.struts.taglib.logic.EmptyTag - JSP - - - Evaluate the nested body content of this tag if the requested variable is - either null or an empty string. -

    - -

    This tag evaluates its nested body content only if the specified value - is either absent (i.e. null), an empty string (i.e. a - java.lang.String with a length of zero), or an empty - java.util.Collection or java.util.Map (tested by - the .isEmpty() method on the respective interface).

    - -

    - JSTL: The equivalent JSTL tag is <c:if> using the - empty operator. For example, -
    - - <c:if test="${empty sessionScope.myBean.myProperty}"> - do something - </c:if> - -

    - -
    Since:
    -
    Struts 1.1
    - ]]> -
    - - name - false - true - - The variable to be compared is the JSP bean specified by this - attribute, if property is not specified, or the value - of the specified property of this bean, if property - is specified.

    - ]]> -
    -
    - - property - false - true - - The variable to be compared is the property (of the bean specified - by the name attribute) specified by this attribute. - The property reference can be simple, nested, and/or indexed.

    - ]]> -
    -
    - - scope - false - true - - The bean scope within which to search for the bean named by the - name property, or "any scope" if not specified.

    - ]]> -
    -
    -
    - - equal - org.apache.struts.taglib.logic.EqualTag - JSP - - - Evaluate the nested body content of this tag if the requested - variable is equal to the specified value. -

    - -

    Compares the variable specified by one of the selector attributes - against the specified constant value. The nested body content of this - tag is evaluated if the variable and value are equal. -

    - ]]> -
    - - cookie - false - true - - The variable to be compared is the value of the cookie whose - name is specified by this attribute.

    - ]]> -
    -
    - - header - false - true - - The variable to be compared is the value of the header whose - name is specified by this attribute. The name match is performed - in a case insensitive manner.

    - ]]> -
    -
    - - name - false - true - - The variable to be compared is the JSP bean specified by this - attribute, if property is not specified, or the value - of the specified property of this bean, if property - is specified.

    - ]]> -
    -
    - - parameter - false - true - - The variable to be compared is the first, or only, value of the - request parameter specified by this attribute.

    - ]]> -
    -
    - - property - false - true - - The variable to be compared is the property (of the bean specified - by the name attribute) specified by this attribute. - The property reference can be simple, nested, and/or indexed.

    - ]]> -
    -
    - - scope - false - true - - The bean scope within which to search for the bean named by the - name property, or "any scope" if not specified.

    - ]]> -
    -
    - - value - true - true - - The constant value to which the variable, specified by other - attribute(s) of this tag, will be compared.

    - ]]> -
    -
    -
    - - forward - org.apache.struts.taglib.logic.ForwardTag - empty - - - Forward control to the page specified by the specified ActionForward - entry. -

    - -

    Performs a PageContext.forward() or - HttpServletResponse.sendRedirect() call for the global - ActionForward entry for the specified name. URL - rewriting will occur automatically if a redirect is performed.

    - ]]> -
    - - name - true - true - - - The logical name of the global ActionForward entry - that identifies the destination, and forwarding approach, to be used. - Note: forwarding to Tiles definitions is not supported - from this tag. You should forward to them from an Action subclass. -

    - ]]> -
    -
    -
    - - greaterEqual - org.apache.struts.taglib.logic.GreaterEqualTag - JSP - - - Evaluate the nested body content of this tag if the requested - variable is greater than or equal to the specified value. -

    - -

    Compares the variable specified by one of the selector attributes - against the specified constant value. The nested body content of this - tag is evaluated if the variable is greater than or equal - to the value.

    - ]]> -
    - - cookie - false - true - - The variable to be compared is the value of the cookie whose - name is specified by this attribute.

    - ]]> -
    -
    - - header - false - true - - The variable to be compared is the value of the header whose - name is specified by this attribute. The name match is performed - in a case insensitive manner.

    - ]]> -
    -
    - - name - false - true - - The variable to be compared is the JSP bean specified by this - attribute, if property is not specified, or the value - of the specified property of this bean, if property - is specified.

    - ]]> -
    -
    - - parameter - false - true - - The variable to be compared is the first, or only, value of the - request parameter specified by this attribute.

    - ]]> -
    -
    - - property - false - true - - The variable to be compared is the property (of the bean specified - by the name attribute) specified by this attribute. - The property reference can be simple, nested, and/or indexed.

    - ]]> -
    -
    - - scope - false - true - - The bean scope within which to search for the bean named by the - name property, or "any scope" if not specified.

    - ]]> -
    -
    - - value - true - true - - The constant value to which the variable, specified by other - attribute(s) of this tag, will be compared.

    - ]]> -
    -
    -
    - - greaterThan - org.apache.struts.taglib.logic.GreaterThanTag - JSP - - - Evaluate the nested body content of this tag if the requested - variable is greater than the specified value. -

    - -

    Compares the variable specified by one of the selector attributes - against the specified constant value. The nested body content of this - tag is evaluated if the variable is greater than - the value.

    - ]]> -
    - - cookie - false - true - - The variable to be compared is the value of the cookie whose - name is specified by this attribute.

    - ]]> -
    -
    - - header - false - true - - The variable to be compared is the value of the header whose - name is specified by this attribute. The name match is performed - in a case insensitive manner.

    - ]]> -
    -
    - - name - false - true - - The variable to be compared is the JSP bean specified by this - attribute, if property is not specified, or the value - of the specified property of this bean, if property - is specified.

    - ]]> -
    -
    - - parameter - false - true - - The variable to be compared is the first, or only, value of the - request parameter specified by this attribute.

    - ]]> -
    -
    - - property - false - true - - The variable to be compared is the property (of the bean specified - by the name attribute) specified by this attribute. - The property reference can be simple, nested, or indexed.

    - ]]> -
    -
    - - scope - false - true - - The bean scope within which to search for the bean named by the - name property, or "any scope" if not specified.

    - ]]> -
    -
    - - value - true - true - - The constant value to which the variable, specified by other - attribute(s) of this tag, will be compared.

    - ]]> -
    -
    -
    - - iterate - org.apache.struts.taglib.logic.IterateTag - org.apache.struts.taglib.logic.IterateTei - JSP - - - Repeat the nested body content of this tag over a specified collection. -

    - -

    Repeats the nested body content of this tag once for every element - of the specified collection, which must be an Iterator, - a Collection, a Map (whose values are to be - iterated over), or an array. The collection to be iterated over must be - specified in one of the following ways:

    -
      -
    • As a runtime expression specified as the value of the - collection attribute.
    • -
    • As a JSP bean specified by the name attribute.
    • -
    • As the property, specified by the property, of the - JSP bean specified by the name attribute.
    • -
    - -

    The collection to be iterated over MUST conform to one of the following - requirements in order for iteration to be successful:

    -
      -
    • An array of Java objects or primitives.
    • - -
    • An implementation of java.util.Collection, including - ArrayList and Vector.
    • -
    • An implementation of java.util.Enumeration.
    • -
    • An implementation of java.util.Iterator.
    • -
    • An implementation of java.util.Map, including - HashMap, Hashtable, and - TreeMap. NOTE - See below for - additional information about accessing Maps.
    • -
    - -

    Normally, each object exposed by the iterate tag is an element - of the underlying collection you are iterating over. However, if you - iterate over a Map, the exposed object is of type - Map.Entry that has two properties:

    -
      -
    • key - The key under which this item is stored in the - underlying Map.
    • -
    • value - The value that corresponds to this key.
    • -
    - -

    So, if you wish to iterate over the values of a Hashtable, you would - implement code like the following:

    - - <logic:iterate id="element" name="myhashtable">
    - Next element is <bean:write name="element" property="value"/>
    - </logic:iterate> -
    - -

    If the collection you are iterating over can contain null - values, the loop will still be performed but no page scope attribute - (named by the id attribute) will be created for that loop - iteration. You can use the <logic:present> and - <logic:notPresent> tags to test for this case.

    - - ]]> -
    - - collection - false - true - java.lang.Object - - A runtime expression that evaluates to a collection (conforming to - the requirements listed above) to be iterated over.

    - ]]> -
    -
    - - id - true - false - - The name of a page scope JSP bean that will contain the current - element of the collection on each iteration, if it is not - null.

    - ]]> -
    -
    - - indexId - false - false - - The name of a page scope JSP bean that will contain the current - index of the collection on each iteration.

    - ]]> -
    -
    - - length - false - true - - The maximum number of entries (from the underlying collection) to be - iterated through on this page. This can be either an integer that - directly expresses the desired value, or the name of a JSP bean (in - any scope) of type java.lang.Integer that defines the - desired value. If not present, there will be no limit on the number - of iterations performed.

    - ]]> -
    -
    - - name - false - true - - The name of the JSP bean containing the collection to be iterated - (if property is not specified), or the JSP bean whose - property getter returns the collection to be iterated (if - property is specified).

    - ]]> -
    -
    - - offset - false - true - - The zero-relative index of the starting point at which entries from - the underlying collection will be iterated through. This can be either - an integer that directly expresses the desired value, or the name of a - JSP bean (in any scope) of type java.lang.Integer that - defines the desired value. If not present, zero is assumed (meaning - that the collection will be iterated from the beginning.

    - ]]> -
    -
    - - property - false - true - - Name of the property, of the JSP bean specified by name, - whose getter returns the collection to be iterated.

    - ]]> -
    -
    - - scope - false - true - - The bean scope within which to search for the bean named by the - name property, or "any scope" if not specified.

    - ]]> -
    -
    - - type - false - true - - Fully qualified Java class name of the element to be exposed through - the JSP bean named from the id attribute. If not present, - no type conversions will be performed. NOTE: The actual elements of - the collection must be assignment-compatible with this class, or a - request time ClassCastException will occur.

    - ]]> -
    -
    -
    - - lessEqual - org.apache.struts.taglib.logic.LessEqualTag - JSP - - - Evaluate the nested body content of this tag if the requested - variable is less than or equal to the specified value. -

    - -

    Compares the variable specified by one of the selector attributes - against the specified constant value. The nested body content of this - tag is evaluated if the variable is less than or equal - to the value.

    - ]]> -
    - - cookie - false - true - - The variable to be compared is the value of the cookie whose - name is specified by this attribute.

    - ]]> -
    -
    - - header - false - true - - The variable to be compared is the value of the header whose - name is specified by this attribute. The name match is performed - in a case insensitive manner.

    - ]]> -
    -
    - - name - false - true - - The variable to be compared is the JSP bean specified by this - attribute, if property is not specified, or the value - of the specified property of this bean, if property - is specified.

    - ]]> -
    -
    - - parameter - false - true - - The variable to be compared is the first, or only, value of the - request parameter specified by this attribute.

    - ]]> -
    -
    - - property - false - true - - The variable to be compared is the property (of the bean specified - by the name attribute) specified by this attribute. - The property reference can be simple, nested, or indexed.

    - ]]> -
    -
    - - scope - false - true - - The bean scope within which to search for the bean named by the - name property, or "any scope" if not specified.

    - ]]> -
    -
    - - value - true - true - - The constant value to which the variable, specified by other - attribute(s) of this tag, will be compared.

    - ]]> -
    -
    -
    - - lessThan - org.apache.struts.taglib.logic.LessThanTag - JSP - - - Evaluate the nested body content of this tag if the requested - variable is less than the specified value. -

    - -

    Compares the variable specified by one of the selector attributes - against the specified constant value. The nested body content of this - tag is evaluated if the variable is less than - the value.

    - ]]> -
    - - cookie - false - true - - The variable to be compared is the value of the cookie whose - name is specified by this attribute.

    - ]]> -
    -
    - - header - false - true - - The variable to be compared is the value of the header whose - name is specified by this attribute. The name match is performed - in a case insensitive manner.

    - ]]> -
    -
    - - name - false - true - - The variable to be compared is the JSP bean specified by this - attribute, if property is not specified, or the value - of the specified property of this bean, if property - is specified.

    - ]]> -
    -
    - - parameter - false - true - - The variable to be compared is the first, or only, value of the - request parameter specified by this attribute.

    - ]]> -
    -
    - - property - false - true - - The variable to be compared is the property (of the bean specified - by the name attribute) specified by this attribute. - The property reference can be simple, nested, and/or indexed.

    - ]]> -
    -
    - - scope - false - true - - The bean scope within which to search for the bean named by the - name property, or "any scope" if not specified.

    - ]]> -
    -
    - - value - true - true - - The constant value to which the variable, specified by other - attribute(s) of this tag, will be compared.

    - ]]> -
    -
    -
    - - match - org.apache.struts.taglib.logic.MatchTag - JSP - - - Evaluate the nested body content of this tag if the specified value - is an appropriate substring of the requested variable. -

    - -

    Matches the variable specified by one of the selector attributes - (as a String) against the specified constant value. If the value is - a substring (appropriately limited by the location - attribute), the nested body content of this tag is evaluated.

    - ]]> -
    - - cookie - false - true - - The variable to be matched is the value of the cookie whose - name is specified by this attribute.

    - ]]> -
    -
    - - header - false - true - - The variable to be matched is the value of the header whose - name is specified by this attribute. The name match is performed - in a case insensitive manner.

    - ]]> -
    -
    - - location - false - true - - If not specified, a match between the variable and the value may - occur at any position within the variable string. If specified, the - match must occur at the specified location (either start - or end) of the variable string.

    - ]]> -
    -
    - - name - false - true - - The variable to be matched is the JSP bean specified by this - attribute, if property is not specified, or the value - of the specified property of this bean, if property - is specified.

    - ]]> -
    -
    - - parameter - false - true - - The variable to be matched is the first, or only, value of the - request parameter specified by this attribute.

    - ]]> -
    -
    - - property - false - true - - The variable to be matched is the property (of the bean specified - by the name attribute) specified by this attribute. - The property reference can be simple, nested, and/or indexed.

    - ]]> -
    -
    - - scope - false - true - - The bean scope within which to search for the bean named by the - name property, or "any scope" if not specified.

    - ]]> -
    -
    - - value - true - true - - The constant value which is checked for existence as a substring - of the specified variable.

    - ]]> -
    -
    -
    - - messagesNotPresent - org.apache.struts.taglib.logic.MessagesNotPresentTag - JSP - - - Generate the nested body content of this tag if the specified - message is not present in any scope. -

    - -

    Evaluates the nested body content of this tag if - an ActionMessages - object, ActionErrors object, a String, - or a String array is not present in any scope. If - such a bean is found, nothing will be rendered. -

    - -
    Since:
    -
    Struts 1.1
    - ]]> -
    - - name - false - true - - The parameter key used to retrieve the message from page, request, - session or application scope.

    - ]]> -
    -
    - - property - false - true - - Name of the property for which messages should be - retrieved. If not specified, all messages (regardless - of property) are retrieved. -

    - ]]> -
    -
    - - message - false - true - - By default the tag will retrieve the bean it will - iterate over from the Globals.ERROR_KEY constant string, - but if this attribute is set to 'true' the bean - will be retrieved from the Globals.MESSAGE_KEY - constant string. Also if this is set to 'true', any value - assigned to the name attribute will be ignored. -

    - ]]> -
    -
    -
    - - messagesPresent - org.apache.struts.taglib.logic.MessagesPresentTag - JSP - - - Generate the nested body content of this tag if the specified - message is present in any scope. -

    - -

    Evaluates the nested body content of this tag if - an ActionMessages - object, ActionErrors object, a String, - or a String array is present in any scope. If - such a bean is not found, nothing will be rendered. -

    - -
    Since:
    -
    Struts 1.1
    - ]]> -
    - - name - false - true - - The parameter key used to retrieve the message from page, request, - session, or application scope.

    - ]]> -
    -
    - - property - false - true - - Name of the property for which messages should be - retrieved. If not specified, all messages (regardless - of property) are retrieved. -

    - ]]> -
    -
    - - message - false - true - - By default the tag will retrieve the bean it will - iterate over from the Globals.ERROR_KEY constant string, - but if this attribute is set to 'true' the bean - will be retrieved from the Globals.MESSAGE_KEY - constant string. Also if this is set to 'true', any value - assigned to the name attribute will be ignored. -

    - ]]> -
    -
    -
    - - notEmpty - org.apache.struts.taglib.logic.NotEmptyTag - JSP - - - Evaluate the nested body content of this tag if the requested variable is - neither null, nor an empty string, nor an empty java.util.Collection - (tested by the .isEmpty() method on the java.util.Collection interface). -

    - -

    This tag evaluates its nested body content only if the specified value - is present (i.e. not null) and is not an empty string (i.e. a - java.lang.String with a length of zero).

    - -

    - JSTL: The equivalent JSTL tag is <c:if> using the - ! empty operator. For example, -
    - - <c:if test="${ ! empty sessionScope.myBean.myProperty}"> - do something - </c:if> - -

    - ]]> -
    - - name - false - true - - The variable to be compared is the JSP bean specified by this - attribute, if property is not specified, or the value - of the specified property of this bean, if property - is specified.

    - ]]> -
    -
    - - property - false - true - - The variable to be compared is the property (of the bean specified - by the name attribute) specified by this attribute. - The property reference can be simple, nested, and/or indexed.

    - ]]> -
    -
    - - scope - false - true - - The bean scope within which to search for the bean named by the - name property, or "any scope" if not specified.

    - ]]> -
    -
    -
    - - notEqual - org.apache.struts.taglib.logic.NotEqualTag - JSP - - - Evaluate the nested body content of this tag if the requested - variable is not equal to the specified value. -

    - -

    Compares the variable specified by one of the selector attributes - against the specified constant value. The nested body content of this - tag is evaluated if the variable and value are not equal. -

    - ]]> -
    - - cookie - false - true - - The variable to be compared is the value of the cookie whose - name is specified by this attribute.

    - ]]> -
    -
    - - header - false - true - - The variable to be compared is the value of the header whose - name is specified by this attribute. The name match is performed - in a case insensitive manner.

    - ]]> -
    -
    - - name - false - true - - The variable to be compared is the JSP bean specified by this - attribute, if property is not specified, or the value - of the specified property of this bean, if property - is specified.

    - ]]> -
    -
    - - parameter - false - true - - The variable to be compared is the first, or only, value of the - request parameter specified by this attribute.

    - ]]> -
    -
    - - property - false - true - - The variable to be compared is the property (of the bean specified - by the name attribute) specified by this attribute. - The property reference can be simple, nested, and/or indexed.

    - ]]> -
    -
    - - scope - false - true - - The bean scope within which to search for the bean named by the - name property, or "any scope" if not specified.

    - ]]> -
    -
    - - value - true - true - - The constant value to which the variable, specified by other - attribute(s) of this tag, will be compared.

    - ]]> -
    -
    -
    - - notMatch - org.apache.struts.taglib.logic.NotMatchTag - JSP - - - Evaluate the nested body content of this tag if the specified value - is not an appropriate substring of the requested variable. -

    - -

    Matches the variable specified by one of the selector attributes - (as a String) against the specified constant value. If the value is - not a substring (appropriately limited by the location - attribute), the nested body content of this tag is evaluated.

    - ]]> -
    - - cookie - false - true - - The variable to be matched is the value of the cookie whose - name is specified by this attribute.

    - ]]> -
    -
    - - header - false - true - - The variable to be matched is the value of the header whose - name is specified by this attribute. The name match is performed - in a case insensitive manner.

    - ]]> -
    -
    - - location - false - true - - If not specified, a match between the variable and the value may - occur at any position within the variable string. If specified, the - match must occur at the specified location (either start - or end) of the variable string.

    - ]]> -
    -
    - - name - false - true - - The variable to be matched is the JSP bean specified by this - attribute, if property is not specified, or the value - of the specified property of this bean, if property - is specified.

    - ]]> -
    -
    - - parameter - false - true - - The variable to be matched is the first, or only, value of the - request parameter specified by this attribute.

    - ]]> -
    -
    - - property - false - true - - The variable to be matched is the property (of the bean specified - by the name attribute) specified by this attribute. - The property reference can be simple, nested, and/or indexed.

    - ]]> -
    -
    - - scope - false - true - - The bean scope within which to search for the bean named by the - name property, or "any scope" if not specified.

    - ]]> -
    -
    - - value - true - true - - The constant value which is checked for existence as a substring - of the specified variable.

    - ]]> -
    -
    -
    - - notPresent - org.apache.struts.taglib.logic.NotPresentTag - JSP - - - Generate the nested body content of this tag if the specified - value is not present in this request. -

    - -

    Depending on which attribute is specified, this tag checks the - current request, and evaluates the nested body content of this tag - only if the specified value is not present. Only one - of the attributes may be used in one occurrence of this tag, unless - you use the property attribute, in which case the - name attribute is also required.

    - ]]> -
    - - cookie - false - true - - Checks for the existence of a cookie with the specified name.

    - ]]> -
    -
    - - header - false - true - - Checks for the existence of an HTTP header with the specified - name. The name match is performed in a case insensitive manner.

    - ]]> -
    -
    - - name - false - true - - Checks for the existence of a JSP bean, in any scope, with the - specified name. If property is also specified, checks - for a non-null property value for the specified property.

    - ]]> -
    -
    - - parameter - false - true - - Checks for the existence of at least one occurrence of the - specified request parameter on this request, even if the parameter - value is a zero-length string.

    - ]]> -
    -
    - - property - false - true - - Checks for the existence of a non-null property value, returned - by a property getter method on the JSP bean (in any scope) that is - specified by the name attribute. Property references - can be simple, nested, and/or indexed.

    - ]]> -
    -
    - - role - false - true - - Checks whether the currently authenticated user (if any) has been - associated with the specified security role.

    - ]]> -
    -
    - - scope - false - true - - The bean scope within which to search for the bean named by the - name property, or "any scope" if not specified.

    - ]]> -
    -
    - - user - false - true - - Checks whether the currently authenticated user principal has the - specified name.

    - ]]> -
    -
    -
    - - present - org.apache.struts.taglib.logic.PresentTag - JSP - - - Generate the nested body content of this tag if the specified - value is present in this request. -

    - -

    Depending on which attribute is specified, this tag checks the - current request, and evaluates the nested body content of this tag - only if the specified value is present. Only one - of the attributes may be used in one occurrence of this tag, unless - you use the property attribute, in which case the - name attribute is also required.

    - ]]> -
    - - cookie - false - true - - Checks for the existence of a cookie with the specified name.

    - ]]> -
    -
    - - header - false - true - - Checks for the existence of an HTTP header with the specified - name. The name match is performed in a case insensitive manner.

    - ]]> -
    -
    - - name - false - true - - Checks for the existence of a JSP bean, in any scope, with the - specified name. If property is also specified, checks - for a non-null property value for the specified property.

    - ]]> -
    -
    - - parameter - false - true - - Checks for the existence of at least one occurrence of the - specified request parameter on this request, even if the parameter - value is a zero-length string.

    - ]]> -
    -
    - - property - false - true - - Checks for the existence of a non-null property value, returned - by a property getter method on the JSP bean (in any scope) that is - specified by the name attribute. Property references - can be simple, nested, and/or indexed.

    - ]]> -
    -
    - - role - false - true - - Checks whether the currently authenticated user (if any) has been - associated with any of the specified security roles. Use a comma-delimited - list to check for multiple roles. Example: - <logic:present role="role1,role2,role3"> - code..... - </logic:present>

    - ]]> -
    -
    - - scope - false - true - - The bean scope within which to search for the bean named by the - name property, or "any scope" if not specified.

    - ]]> -
    -
    - - user - false - true - - Checks whether the currently authenticated user principal has the - specified name.

    - ]]> -
    -
    -
    - - redirect - org.apache.struts.taglib.logic.RedirectTag - - Render an HTTP Redirect

    - - -

    Performs an HttpServletResponse.sendRedirect() - call to the hyperlink specified by the attributes to this - tag. URL rewriting will be applied automatically, to - maintain session state in the absence of cookies.

    - -

    The base URL for this redirect is calculated based on - which of the following attributes you specify (you must - specify exactly one of them):

    -
      -
    • forward - Use the value of this attribute as the - name of a global ActionForward to be looked - up, and use the module-relative or context-relative - URI found there.
    • -
    • href - Use the value of this attribute unchanged. -
    • -
    • page - Use the value of this attribute as an - module-relative URI, and generate a server-relative - URI by including the context path.
    • -
    - -

    Normally, the redirect you specify with one of the - attributes described in the previous paragraph will be left - unchanged (other than URL rewriting if necessary). However, - there are two ways you can append one or more dynamically - defined query parameters to the hyperlink -- specify a single - parameter with the paramId attribute (and its - associated attributes to select the value), or specify the - name (and optional property) - attributes to select a java.util.Map bean that - contains one or more parameter ids and corresponding values. -

    - -

    To specify a single parameter, use the paramId - attribute to define the name of the request parameter to be - submitted. To specify the corresponding value, use one of the - following approaches:

    -
      -
    • Specify only the paramName attribute - - The named JSP bean (optionally scoped by the value of the - paramScope attribute) must identify a value - that can be converted to a String.
    • -
    • Specify both the paramName and - paramProperty attributes - The specified - property getter method will be called on the JSP bean - identified by the paramName (and optional - paramScope) attributes, in order to select - a value that can be converted to a String.
    • -
    - -

    If you prefer to specify a java.util.Map that - contains all of the request parameters to be added to the - hyperlink, use one of the following techniques:

    -
      -
    • Specify only the name attribute - - The named JSP bean (optionally scoped by the value of - the scope attribute) must identify a - java.util.Map containing the parameters.
    • -
    • Specify both name and - property attributes - The specified - property getter method will be called on the bean - identified by the name (and optional - scope) attributes, in order to return the - java.util.Map containing the parameters.
    • -
    - -

    As the Map is processed, the keys are assumed - to be the names of query parameters to be appended to the - hyperlink. The value associated with each key must be either - a String or a String array representing the parameter value(s). - If a String array is specified, more than one value for the - same query parameter name will be created.

    - ]]> -
    - - action - false - true - - Logical name of a global Action that - contains the actual content-relative URI of the destination - of this transfer. This hyperlink may be dynamically - modified by the inclusion of query parameters, as described - in the tag description. You must specify - exactly one of the action attribute, the - forward attribute, the - href attribute, - or the page attribute.

    - ]]> -
    -
    - - anchor - false - true - - Optional anchor tag ("#xxx") to be added to the generated - hyperlink. Specify this value without any - "#" character.

    - ]]> -
    -
    - - forward - false - true - - Logical name of a global ActionForward that - contains the actual content-relative URI of the destination - of this redirect. This URI may be dynamically - modified by the inclusion of query parameters, as described - in the tag description. You must specify - exactly one of the forward attribute, the - href attribute, the linkName - attribute, or the page attribute.

    - ]]> -
    -
    - - href - false - true - - The URL to which this redirect will transfer control. - This URL may be dynamically modified - by the inclusion of query parameters, as described in the - tag description. You must specify - exactly one of the forward attribute, the - href attribute, the linkName - attribute, or the page attribute.

    - ]]> -
    -
    - - name - false - true - - The name of a JSP bean that contains a Map - representing the query parameters (if property - is not specified), or a JSP bean whose property getter is - called to return a Map (if property - is specified).

    - ]]> -
    -
    - - page - false - true - - The context-relative path (beginning with a "/" - character) to which this hyperlink will transfer control - if activated. This hyperlink may be dynamically modified - by the inclusion of query parameters, as described in the - tag description. You must specify exactly - one of the forward attribute, the - href attribute, the linkName - attribute, or the page attribute.

    - ]]> -
    -
    - - paramId - false - true - - The name of the request parameter that will be dynamically - added to the generated hyperlink. The corresponding value is - defined by the paramName and (optional) - paramProperty attributes, optionally scoped by - the paramScope attribute

    - ]]> -
    -
    - - paramName - false - true - - The name of a JSP bean that is a String containing the - value for the request parameter named by paramId - (if paramProperty is not specified), or a JSP - bean whose property getter is called to return a String - (if paramProperty is specified). The JSP bean - is constrained to the bean scope specified by the - paramScope property, if it is specified.

    - ]]> -
    -
    - - paramProperty - false - true - - The name of a property of the bean specified by the - paramName attribute, whose return value must - be a String containing the value of the request parameter - (named by the paramId attribute) that will be - dynamically added to this hyperlink.

    - ]]> -
    -
    - - paramScope - false - true - - The scope within which to search for the bean specified - by the paramName attribute. If not specified, - all scopes are searched.

    - ]]> -
    -
    - - property - false - true - - The name of a property of the bean specified by the - name attribute, whose return value must be - a java.util.Map containing the query parameters - to be added to the hyperlink. You must - specify the name attribute if you specify - this attribute.

    - ]]> -
    -
    - - scope - false - true - - The scope within which to search for the bean specified - by the name attribute. If not specified, all - scopes are searched.

    - ]]> -
    -
    - - transaction - false - true - boolean - - Set to true if you want the current - transaction control token included in the generated - URL for this redirect.

    - ]]> -
    -
    - - useLocalEncoding - false - true - boolean - - If set to true, LocalCharacterEncoding will be - used, that is, the characterEncoding set to the HttpServletResponse, - as prefered character encoding rather than UTF-8, when - URLEncoding is done on parameters of the URL.

    - ]]> -
    -
    -
    -
    - - - diff --git a/oeqPrimaryB2/src/main/webapp/taglibs/config/struts/struts-nested.tld b/oeqPrimaryB2/src/main/webapp/taglibs/config/struts/struts-nested.tld deleted file mode 100644 index 20a8c0a..0000000 --- a/oeqPrimaryB2/src/main/webapp/taglibs/config/struts/struts-nested.tld +++ /dev/null @@ -1,5051 +0,0 @@ - - - - - 1.3 - 1.2 - nested - http://struts.apache.org/tags-nested - - [Since Struts 1.1]

    -

    This tag library brings a nested context to the functionality of the - Struts custom tag library.

    - -

    It's written in a layer that extends the current Struts tags, building on - their logic and functionality. The layer enables the tags to be aware of the - tags which surround them so they can correctly provide the nesting property - reference to the Struts system. -

    - -

    It's all about nesting beans...
    - A bean holds a reference to another bean internally, and all access to that - bean is handled through the current bean. This act of having one bean's - access go through another bean is known as "nesting beans". The first bean - is known as the parent bean. The bean which it references, is known as a - child bean. The terms "parent" and "child" are commonly used to describe the - model's hierarchy. -

    - -

    A simple example...
    - Take an object which represents a monkey. The monkey's job is to pick - bunches of bananas. On each bunch picked hangs many bananas. If this case - was translated to bean objects, the monkey object would have a reference to - the bunch objects he picked, and each bunch object would hold a reference - to the bananas hanging in the bunch. -

    - -

    To describe this...
    - The monkey object is the parent to the bunch object, and the bunch object - is a child of the monkey object. The bunch object is parent to its child - banana objects, and the child banana objects children of the bunch object. - The monkey is higher in the hierarchy than the bananas, and the bananas - lower in the hierarchy to the bunches. -

    - -

    One special term to remember is for the most parent class, which is known - as the "root" object which starts the hierarchy.

    - -

    Nested tags are all about efficiently managing this style of hierarchy - structure within your JSP markup.

    - -

    - Important Note: Nearly all these tags extend tags from - other libraries to bring their functionality into the nested context. - Nesting relies on the tags working against the one bean model, and managing - the properties so that they become relative to the properties they are - nested within. In doing so, the tags will set the "name" attribute internally - (where applicable), and in many cases will rely on the "property" attribute - being set so it can be updated internally to become nested. The original tags - on occasion provide options that don't use the "name" and "property" - attributes. These uses will then fall outside the nested context, and will - most likely cause error. To take advantage of these options, markup using - the original tag for these cases. For an example see the - <nested:options> tag. -

    - ]]> -
    - - nest - org.apache.struts.taglib.nested.NestedPropertyTag - JSP - - - Defines a new level of nesting for child tags to reference to -

    - -

    - This tag provides a simple method of defining a logical nesting level in - the nested hierarchy. It run no explicit logic, is simply a place holder. - It also means you can remove the need for explicit setting of level - properties in child tags. -

    -

    - Just as the iterate tag provide a parent to other tags, this does the same - but there is no logic for iterating or otherwise. -

    -

    - Example...

    -
    -<nested:write property="myNestedLevel.propertyOne" />
    -<nested:write property="myNestedLevel.propertyTwo" />
    -<nested:write property="myNestedLevel.propertyThree" />
    -      
    -

    Can instead become...

    -
    -<nested:nest property="myNestedLevel" >
    -  <nested:write property="propertyOne" />
    -  <nested:write property="propertyTwo" />
    -  <nested:write property="propertyThree" />
    -</nested:nest >
    -      
    - ]]> -
    - - property - false - true - - - - -
    - - writeNesting - org.apache.struts.taglib.nested.NestedWriteNestingTag - org.apache.struts.taglib.nested.NestedWriteNestingTei - JSP - - - Writes or makes a scripting variable of the current nesting level. -

    - - This tag provides a way of accessing the nested property reference used by - the nested tags. Can expose a scripting variable, or simply write out the - value. - ]]> -
    - - property - false - true - - - - - - id - false - true - - id is supplied, then what would have been written out into the - response stream, will instead be made available as a String object - defined by the variable name provided. - ]]> - - - - filter - false - true - boolean - - - - -
    - - root - org.apache.struts.taglib.nested.NestedRootTag - JSP - - To start off a nested hierarchy without the need for a form

    - -

    - This tag is provided to allow the nested tags to find a common bean - reference without the need for a form and its relative overhead. As - long as the name attribute of this tag matches the name - of a bean in scope of the JSP (ie: Struts tags can find it via usual - means). For example you can load a bean for use with the - jsp:useBean tag. -

    -

    - The tag can also be used without specifying the name - attribute, but this is only in the case that the current JSP is a - dynamic include specified in another file. You will not be able to run - the tag without a name unless this inclusion is in place. Otherwise - the nested tags will not have the bean and property references that they - need to provide their logic. -

    -

    - Note: The access to a bean via the name - attribute takes priority over looking for the reference from other - parent tags. So if a name is specified, a bean will have to be there - waiting for it. It was made this way so that you could use separate - beans within a JSP that itself is an inclusion into another. -

    - ]]> -
    - - name - false - true - - - - -
    - - define - org.apache.struts.taglib.nested.bean.NestedDefineTag - org.apache.struts.taglib.nested.bean.NestedDefineTei - empty - - Nested Extension - - Define a scripting variable based on the value(s) of the specified - bean property. -

    - -

    This tag is an extension of the - <bean:define> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - id - true - true - - - - name - false - true - - - - property - false - true - - - - scope - false - true - - - - toScope - false - true - - - - type - false - true - - - - value - false - true - - -
    - - message - org.apache.struts.taglib.nested.bean.NestedMessageTag - empty - - Nested Extension - - Render an internationalized message string to the response. -

    - -

    This tag is an extension of the - <bean:message> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - arg0 - false - true - - - - arg1 - false - true - - - - arg2 - false - true - - - - arg3 - false - true - - - - arg4 - false - true - - - - bundle - false - true - - - - key - false - true - - - - locale - false - true - - - - name - false - true - - - - property - false - true - - - - scope - false - true - - -
    - - size - org.apache.struts.taglib.nested.bean.NestedSizeTag - org.apache.struts.taglib.bean.SizeTei - empty - - Nested Extension - - Define a bean containing the number of elements in a Collection or Map. -

    - -

    This tag is an extension of the - <bean:size> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - collection - false - true - java.lang.Object - - - - id - true - true - - - - name - false - true - - - - property - false - true - - - - scope - false - true - - -
    - - write - org.apache.struts.taglib.nested.bean.NestedWriteTag - empty - - Nested Extension - - Render the value of the specified bean property to the current - JspWriter. -

    - -

    This tag is an extension of the - <bean:write> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - bundle - false - true - - - - filter - false - true - boolean - - - - format - false - true - - - - formatKey - false - true - - - - ignore - false - true - boolean - - - - locale - false - true - - - - name - false - true - - - - property - false - true - - - - scope - false - true - - -
    - - checkbox - org.apache.struts.taglib.nested.html.NestedCheckboxTag - - Nested Extension - Render A Checkbox Input Field

    - -

    This tag is an extension of the - <html:checkbox> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - accesskey - false - true - - - - alt - false - true - - - - altKey - false - true - - - - bundle - false - true - -
    Since:
    -
    Struts 1.2.7
    - ]]> -
    -
    - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - disabled - false - true - boolean - - - - errorKey - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyle - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleClass - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleId - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - indexed - false - true - boolean - - - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - name - false - true - - - - onblur - false - true - - - - onchange - false - true - - - - onclick - false - true - - - - ondblclick - false - true - - - - onfocus - false - true - - - - onkeydown - false - true - - - - onkeypress - false - true - - - - onkeyup - false - true - - - - onmousedown - false - true - - - - onmousemove - false - true - - - - onmouseout - false - true - - - - onmouseover - false - true - - - - onmouseup - false - true - - - - property - true - true - - - - style - false - true - - - - styleClass - false - true - - - - styleId - false - true - - - - tabindex - false - true - - - - title - false - true - - - - titleKey - false - true - - - - value - false - true - - -
    - - errors - org.apache.struts.taglib.nested.html.NestedErrorsTag - empty - - - Nested Extension - Conditionally display a set of accumulated error messages. -

    - -

    This tag is an extension of the - <html:errors> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - bundle - false - true - - - - footer - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - header - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - locale - false - true - - - - name - false - true - - - - prefix - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - property - false - true - - - - suffix - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    -
    - - file - org.apache.struts.taglib.nested.html.NestedFileTag - - Nested Extension - - Render A File Select Input Field -

    - -

    This tag is an extension of the - <html:file> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - accesskey - false - true - - - - accept - false - true - - - - alt - false - true - - - - altKey - false - true - - - - bundle - false - true - -
    Since:
    -
    Struts 1.2.7
    - ]]> -
    -
    - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - disabled - false - true - boolean - - - - errorKey - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyle - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleClass - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleId - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - indexed - false - true - boolean - - - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - maxlength - false - true - - - - name - false - true - - - - onblur - false - true - - - - onchange - false - true - - - - onclick - false - true - - - - ondblclick - false - true - - - - onfocus - false - true - - - - onkeydown - false - true - - - - onkeypress - false - true - - - - onkeyup - false - true - - - - onmousedown - false - true - - - - onmousemove - false - true - - - - onmouseout - false - true - - - - onmouseover - false - true - - - - onmouseup - false - true - - - - property - true - true - - - - size - false - true - - - - style - false - true - - - - styleClass - false - true - - - - styleId - false - true - - - - tabindex - false - true - - - - title - false - true - - - - titleKey - false - true - - - - value - false - true - - -
    - - form - org.apache.struts.taglib.nested.html.NestedFormTag - JSP - - Nested Extension - Define An Input Form

    - -

    This tag is an extension of the - <html:form> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - action - true - true - - - - acceptCharset - false - true - - - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - disabled - false - true - boolean - -
    Since:
    -
    Struts 1.2.7
    - ]]> -
    -
    - - enctype - false - true - - - - focus - false - true - - - - focusIndex - false - true - - - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - method - false - true - - - - onreset - false - true - - - - onsubmit - false - true - - - - readonly - false - true - boolean - -
    Since:
    -
    Struts 1.2.7
    - ]]> -
    -
    - - scriptLanguage - false - true - boolean - - - - style - false - true - - - - styleClass - false - true - - - - styleId - false - true - - - - target - false - true - - -
    - - hidden - org.apache.struts.taglib.nested.html.NestedHiddenTag - - Nested Extension - - Render A Hidden Field -

    - -

    This tag is an extension of the - <html:hidden> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - alt - false - true - - - - altKey - false - true - - - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - indexed - false - true - boolean - - - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - name - false - true - - - - property - true - true - - - - title - false - true - - - - titleKey - false - true - - - - styleClass - false - true - - - - styleId - false - true - - - - value - false - true - - - - write - false - true - boolean - - -
    - - image - org.apache.struts.taglib.nested.html.NestedImageTag - - Nested Extension - - Render an input tag of type "image" -

    - -

    This tag is an extension of the - <html:image> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - accesskey - false - true - - - - align - false - true - - - - alt - false - true - - - - altKey - false - true - - - - border - false - true - - - - bundle - false - true - - - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - disabled - false - true - boolean - - - - indexed - false - true - boolean - - - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - locale - false - true - - - - module - false - true - - - onblur - false - true - - - - onchange - false - true - - - - onclick - false - true - - - - ondblclick - false - true - - - - onfocus - false - true - - - - onkeydown - false - true - - - - onkeypress - false - true - - - - onkeyup - false - true - - - - onmousedown - false - true - - - - onmousemove - false - true - - - - onmouseout - false - true - - - - onmouseover - false - true - - - - onmouseup - false - true - - - - page - false - true - - - - pageKey - false - true - - - - property - false - true - - - - src - false - true - - - - srcKey - false - true - - - - style - false - true - - - - styleClass - false - true - - - - tabindex - false - true - - - - title - false - true - - - - titleKey - false - true - - - - value - false - true - - -
    - - img - org.apache.struts.taglib.nested.html.NestedImgTag - empty - - Nested Extension - Render an HTML "img" tag

    - -

    This tag is an extension of the - <html:img> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - accesskey - false - true - - - - align - false - true - - - - alt - false - true - - - - altKey - false - true - - - - border - false - true - - - - bundle - false - true - - - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - height - false - true - - - - hspace - false - true - - - - imageName - false - true - - - - ismap - false - true - - - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - locale - false - true - - - - name - false - true - - - - onkeydown - false - true - - - - onkeypress - false - true - - - - onkeyup - false - true - - - - paramId - false - true - - - - page - false - true - - - - pageKey - false - true - - - - action - false - true - - - - module - false - true - - - - paramName - false - true - - - - paramProperty - false - true - - - - paramScope - false - true - - - - property - false - true - - - - scope - false - true - - - - src - false - true - - - - srcKey - false - true - - - - style - false - true - - - - styleClass - false - true - - - - styleId - false - true - - - - title - false - true - - - - titleKey - false - true - - - - useLocalEncoding - false - true - boolean - - - - usemap - false - true - - - - vspace - false - true - - - - width - false - true - - -
    - - link - org.apache.struts.taglib.nested.html.NestedLinkTag - JSP - - Nested Extension - Render an HTML anchor or hyperlink

    - -

    This tag is an extension of the - <html:link> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - accesskey - false - true - - - - action - false - true - - - - module - false - true - - - - anchor - false - true - - - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - forward - false - true - - - - href - false - true - - - - indexed - false - true - boolean - - - - indexId - false - true - - - - bundle - false - true - -
    Since:
    -
    Struts 1.2.7
    - ]]> -
    -
    - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - linkName - false - true - - - - name - false - true - - - - onblur - false - true - - - - onclick - false - true - - - - ondblclick - false - true - - - - onfocus - false - true - - - - onkeydown - false - true - - - - onkeypress - false - true - - - - onkeyup - false - true - - - - onmousedown - false - true - - - - onmousemove - false - true - - - - onmouseout - false - true - - - - onmouseover - false - true - - - - onmouseup - false - true - - - - page - false - true - - - - paramId - false - true - - - - paramName - false - true - - - - paramProperty - false - true - - - - paramScope - false - true - - - - property - false - true - - - - scope - false - true - - - - style - false - true - - - - styleClass - false - true - - - - styleId - false - true - - - - tabindex - false - true - - - - target - false - true - - - - title - false - true - - - - titleKey - false - true - - - - transaction - false - true - boolean - - - - useLocalEncoding - false - true - boolean - - -
    - - messages - org.apache.struts.taglib.nested.html.NestedMessagesTag - org.apache.struts.taglib.html.MessagesTei - JSP - - - Nested Extension - Conditionally display a set of accumulated messages. -

    - -

    This tag is an extension of the - <html:messages> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - id - true - true - - - - bundle - false - true - - - - locale - false - true - - - - name - false - true - - - - property - false - true - - - - header - false - true - - - - footer - false - true - - - - message - false - true - - -
    - - multibox - org.apache.struts.taglib.nested.html.NestedMultiboxTag - - Nested Extension - - Render A Checkbox Input Field -

    - -

    This tag is an extension of the - <html:multibox> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - accesskey - false - true - - - - alt - false - true - - - - altKey - false - true - - - - bundle - false - true - -
    Since:
    -
    Struts 1.2.7
    - ]]> -
    -
    - - disabled - false - true - boolean - - - - errorKey - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyle - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleClass - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleId - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - name - false - true - - - - onblur - false - true - - - - onchange - false - true - - - - onclick - false - true - - - - ondblclick - false - true - - - - onfocus - false - true - - - - onkeydown - false - true - - - - onkeypress - false - true - - - - onkeyup - false - true - - - - onmousedown - false - true - - - - onmousemove - false - true - - - - onmouseout - false - true - - - - onmouseover - false - true - - - - onmouseup - false - true - - - - property - true - true - - - - style - false - true - - - - styleClass - false - true - - - - styleId - false - true - - - - tabindex - false - true - - - - title - false - true - - - - titleKey - false - true - - - - value - false - true - - -
    - - options - org.apache.struts.taglib.nested.html.NestedOptionsTag - empty - - Nested Extension - Render a Collection of Select Options

    - -

    This tag is an extension of the - <html:options> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    -

    - Note: The nested context of this tag relies on the use - of the "property" property, and the internal use of the "name" property. - The nested tags rely on these properties and will attempt to set them - itself. The <html:options> tag this tag extended - allows other options for the tag which don't use these properties. - To take advantage of these options, markup using the - <html:options> tag instead of the nested tag. -

    -

    - For example, the "collections" option allows you to specify a separate - bean reference which itself is a list of objects with properties - to access the title and value parts of the html option tag. You can use - this in a nested context (the list is a property of a nested bean) by - using the nested define tag and the original options tag. -

    -
    -<nested:nest property="myNestedLevel" />
    -  <nested:define property="collectionList" />
    -  <html:options collection="collectionList"
    -                  property="valueProperty"
    -             labelProperty="labelProperty" />
    -</nested:nest >
    -
    - ]]> -
    - - collection - false - true - java.lang.String - - - - filter - false - true - boolean - - - - labelName - false - true - - - - labelProperty - false - true - - - - name - false - true - - - - property - false - true - - - - style - false - true - - - - styleClass - false - true - - -
    - - optionsCollection - org.apache.struts.taglib.nested.html.NestedOptionsCollectionTag - empty - - Nested Extension - - Render a Collection of Select Options -

    - -

    This tag is an extension of the - <html:optionsCollection> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - filter - false - true - boolean - - - - label - false - true - - - - name - false - true - - - - property - true - true - - - - style - false - true - - - - styleClass - false - true - - - - value - false - true - - -
    - - password - org.apache.struts.taglib.nested.html.NestedPasswordTag - - Nested Extension - - Render A Password Input Field -

    - -

    This tag is an extension of the - <html:password> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - accesskey - false - true - - - - alt - false - true - - - - altKey - false - true - - - - bundle - false - true - -
    Since:
    -
    Struts 1.2.7
    - ]]> -
    -
    - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - disabled - false - true - boolean - - - - errorKey - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyle - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleClass - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleId - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - indexed - false - true - boolean - - - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - maxlength - false - true - - - - name - false - true - - - - onblur - false - true - - - - onchange - false - true - - - - onclick - false - true - - - - ondblclick - false - true - - - - onfocus - false - true - - - - onkeydown - false - true - - - - onkeypress - false - true - - - - onkeyup - false - true - - - - onmousedown - false - true - - - - onmousemove - false - true - - - - onmouseout - false - true - - - - onmouseover - false - true - - - - onmouseup - false - true - - - - onselect - false - true - - - property - true - true - - - - readonly - false - true - boolean - - - - redisplay - false - true - boolean - - - - style - false - true - - - - styleClass - false - true - - - - styleId - false - true - - - - size - false - true - - - - tabindex - false - true - - - - title - false - true - - - - titleKey - false - true - - - - value - false - true - - -
    - - radio - org.apache.struts.taglib.nested.html.NestedRadioTag - - Nested Extension - - Render A Radio Button Input Field -

    - -

    This tag is an extension of the - <html:radio> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - accesskey - false - true - - - - alt - false - true - - - - altKey - false - true - - - - bundle - false - true - -
    Since:
    -
    Struts 1.2.7
    - ]]> -
    -
    - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - disabled - false - true - boolean - - - - errorKey - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyle - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleClass - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleId - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - indexed - false - true - boolean - - - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - name - false - true - - - - onblur - false - true - - - - onchange - false - true - - - - onclick - false - true - - - - ondblclick - false - true - - - - onfocus - false - true - - - - onkeydown - false - true - - - - onkeypress - false - true - - - - onkeyup - false - true - - - - property - true - true - - - - onmousedown - false - true - - - - style - false - true - - - - styleClass - false - true - - - - styleId - false - true - - - - tabindex - false - true - - - - title - false - true - - - - titleKey - false - true - - - - value - true - true - - - - idName - false - true - - -
    - - select - org.apache.struts.taglib.nested.html.NestedSelectTag - JSP - - Nested Extension - - Render A Select Element -

    - -

    This tag is an extension of the - <html:select> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - accesskey - false - true - - - - alt - false - true - - - - altKey - false - true - - - - bundle - false - true - -
    Since:
    -
    Struts 1.2.7
    - ]]> -
    -
    - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - disabled - false - true - boolean - - - - errorKey - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyle - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleClass - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleId - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - indexed - false - true - boolean - - - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - multiple - false - true - - - - name - false - true - - - - onblur - false - true - - - - onchange - false - true - - - - onclick - false - true - - - - ondblclick - false - true - - - - onfocus - false - true - - - - onkeydown - false - true - - - - onkeypress - false - true - - - - onkeyup - false - true - - - - onmousedown - false - true - - - - onmousemove - false - true - - - - onmouseout - false - true - - - - onmouseover - false - true - - - - onmouseup - false - true - - - - property - true - true - - - - style - false - true - - - - styleClass - false - true - - - - styleId - false - true - - - - tabindex - false - true - - - - size - false - true - - - - title - false - true - - - - titleKey - false - true - - - - value - false - true - - -
    - - submit - org.apache.struts.taglib.nested.html.NestedSubmitTag - - Nested Extension - Render A Submit Button

    - -

    This tag is an extension of the - <html:submit> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - accesskey - false - true - - - - alt - false - true - - - - altKey - false - true - - - - bundle - false - true - -
    Since:
    -
    Struts 1.2.7
    - ]]> -
    -
    - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - disabled - false - true - boolean - - - - indexed - false - true - boolean - - - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - onblur - false - true - - - - onchange - false - true - - - - onclick - false - true - - - - ondblclick - false - true - - - - onfocus - false - true - - - - onkeydown - false - true - - - - onkeypress - false - true - - - - onkeyup - false - true - - - - onmousedown - false - true - - - - onmousemove - false - true - - - - onmouseout - false - true - - - - onmouseover - false - true - - - - onmouseup - false - true - - - - property - false - true - - - - style - false - true - - - - styleClass - false - true - - - - styleId - false - true - - - - tabindex - false - true - - - - title - false - true - - - - titleKey - false - true - - - - value - false - true - - -
    - - text - org.apache.struts.taglib.nested.html.NestedTextTag - - Nested Extension - - Render An Input Field of Type text -

    - -

    This tag is an extension of the - <html:text> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - accesskey - false - true - - - - alt - false - true - - - - altKey - false - true - - - - bundle - false - true - -
    Since:
    -
    Struts 1.2.7
    - ]]> -
    -
    - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - disabled - false - true - boolean - - - - errorKey - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyle - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleClass - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleId - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - indexed - false - true - boolean - - - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - maxlength - false - true - - - - name - false - true - - - - onblur - false - true - - - - onchange - false - true - - - - onclick - false - true - - - - ondblclick - false - true - - - - onfocus - false - true - - - - onkeydown - false - true - - - - onkeypress - false - true - - - - onkeyup - false - true - - - - onmousedown - false - true - - - - onmousemove - false - true - - - - onmouseout - false - true - - - - onmouseover - false - true - - - - onmouseup - false - true - - - - onselect - false - true - - - - property - true - true - - - - readonly - false - true - boolean - - - - size - false - true - - - - style - false - true - - - - styleClass - false - true - - - - styleId - false - true - - - - tabindex - false - true - - - - title - false - true - - - - titleKey - false - true - - - - value - false - true - - -
    - - textarea - org.apache.struts.taglib.nested.html.NestedTextareaTag - - Nested Extension - Render A Textarea

    - -

    This tag is an extension of the - <html:textarea> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - accesskey - false - true - - - - alt - false - true - - - - altKey - false - true - - - - bundle - false - true - -
    Since:
    -
    Struts 1.2.7
    - ]]> -
    -
    - - cols - false - true - - - - dir - false - true - - The direction for weak/neutral text for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - disabled - false - true - boolean - - - - errorKey - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyle - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleClass - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - errorStyleId - false - true - -
    Since:
    -
    Struts 1.2.5
    - ]]> -
    -
    - - indexed - false - true - boolean - - - - lang - false - true - - The language code for this element.

    -
    Since:
    -
    Struts 1.3.6
    - ]]> -
    -
    - - name - false - true - - - - onblur - false - true - - - - onchange - false - true - - - - onclick - false - true - - - - ondblclick - false - true - - - - onfocus - false - true - - - - onkeydown - false - true - - - - onkeypress - false - true - - - - onkeyup - false - true - - - - onmousedown - false - true - - - - onmousemove - false - true - - - - onmouseout - false - true - - - - onmouseover - false - true - - - - onmouseup - false - true - - - - onselect - false - true - - - - property - true - true - - - - readonly - false - true - boolean - - - - rows - false - true - - - - style - false - true - - - - styleClass - false - true - - - - styleId - false - true - - - - tabindex - false - true - - - - title - false - true - - - - titleKey - false - true - - - - value - false - true - - -
    - - empty - org.apache.struts.taglib.nested.logic.NestedEmptyTag - JSP - - Nested Extension - - Evaluate the nested body content of this tag if the requested variable is - either null or an empty string. -

    - -

    This tag is an extension of the - <logic:empty> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - name - false - true - - - - property - false - true - - - - scope - false - true - - -
    - - equal - org.apache.struts.taglib.nested.logic.NestedEqualTag - JSP - - Nested Extension - - Evaluate the nested body content of this tag if the requested - variable is equal to the specified value. -

    - -

    This tag is an extension of the - <logic:equal> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - cookie - false - true - - - - header - false - true - - - - name - false - true - - - - parameter - false - true - - - - property - false - true - - - - scope - false - true - - - - value - true - true - - -
    - - greaterEqual - org.apache.struts.taglib.nested.logic.NestedGreaterEqualTag - JSP - - Nested Extension - Evaluate the nested body content of this tag if the requested - variable is greater than or equal to the specified value. -

    - -

    This tag is an extension of the - <logic:greaterEqual> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - cookie - false - true - - - - header - false - true - - - - name - false - true - - - - parameter - false - true - - - - property - false - true - - - - scope - false - true - - - - value - true - true - - -
    - - greaterThan - org.apache.struts.taglib.nested.logic.NestedGreaterThanTag - JSP - - Nested Extension - - Evaluate the nested body content of this tag if the requested - variable is greater than the specified value. -

    - -

    This tag is an extension of the - <logic:greaterThan> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - cookie - false - true - - - - header - false - true - - - - name - false - true - - - - parameter - false - true - - - - property - false - true - - - - scope - false - true - - - - value - true - true - - -
    - - iterate - org.apache.struts.taglib.nested.logic.NestedIterateTag - org.apache.struts.taglib.nested.logic.NestedIterateTei - JSP - - Nested Extension - - Repeat the nested body content of this tag over a specified collection. -

    - -

    This tag is an extension of the - <logic:iterate> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - collection - false - true - java.lang.Object - - - - id - false - true - - - - indexId - false - true - - - - length - false - true - - - - name - false - true - - - - offset - false - true - - - - property - false - true - - - - scope - false - true - - - - type - false - true - - -
    - - lessEqual - org.apache.struts.taglib.nested.logic.NestedLessEqualTag - JSP - - Nested Extension - - Evaluate the nested body content of this tag if the requested - variable is greater than or equal to the specified value. -

    - -

    This tag is an extension of the - <logic:lessEqual> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - cookie - false - true - - - - header - false - true - - - - name - false - true - - - - parameter - false - true - - - - property - false - true - - - - scope - false - true - - - - value - true - true - - -
    - - lessThan - org.apache.struts.taglib.nested.logic.NestedLessThanTag - JSP - - Nested Extension - - Evaluate the nested body content of this tag if the requested - variable is less than the specified value. -

    - -

    This tag is an extension of the - <logic:lessThan> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - cookie - false - true - - - - header - false - true - - - - name - false - true - - - - parameter - false - true - - - - property - false - true - - - - scope - false - true - - - - value - true - true - - -
    - - match - org.apache.struts.taglib.nested.logic.NestedMatchTag - JSP - - Nested Extension - - Evaluate the nested body content of this tag if the specified value - is an appropriate substring of the requested variable. -

    - -

    This tag is an extension of the - <logic:match> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - cookie - false - true - - - - header - false - true - - - - location - false - true - - - - name - false - true - - - - parameter - false - true - - - - property - false - true - - - - scope - false - true - - - - value - true - true - - -
    - - messagesNotPresent - org.apache.struts.taglib.nested.logic.NestedMessagesNotPresentTag - JSP - - - Nested Extension - - Generate the nested body content of this tag if the specified - message is not present in this request. -

    - -

    This tag is an extension of the - <logic:messagesNotPresent> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - name - false - true - - - - property - false - true - - - - message - false - true - - -
    - - messagesPresent - org.apache.struts.taglib.nested.logic.NestedMessagesPresentTag - JSP - - - Nested Extension - - Generate the nested body content of this tag if the specified - message is present in this request. -

    - -

    This tag is an extension of the - <logic:messagesPresent> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - name - false - true - - - - property - false - true - - - - message - false - true - - -
    - - notEmpty - org.apache.struts.taglib.nested.logic.NestedNotEmptyTag - JSP - - Nested Extension - - Evaluate the nested body content of this tag if the requested variable is - neither null nor an empty string. -

    - -

    This tag is an extension of the - <logic:notEmpty> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - name - false - true - - - - property - false - true - - - - scope - false - true - - -
    - - notEqual - org.apache.struts.taglib.nested.logic.NestedNotEqualTag - JSP - - Nested Extension - - Evaluate the nested body content of this tag if the requested - variable is not equal to the specified value. -

    - -

    This tag is an extension of the - <logic:notEqual> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - cookie - false - true - - - - header - false - true - - - - name - false - true - - - - parameter - false - true - - - - property - false - true - - - - scope - false - true - - - - value - true - true - - -
    - - notMatch - org.apache.struts.taglib.nested.logic.NestedNotMatchTag - JSP - - Nested Extension - - Evaluate the nested body content of this tag if the specified value - is not an appropriate substring of the requested variable. -

    - -

    This tag is an extension of the - <logic:notMatch> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - cookie - false - true - - - - header - false - true - - - - location - false - true - - - - name - false - true - - - - parameter - false - true - - - - property - false - true - - - - scope - false - true - - - - value - true - true - - -
    - - notPresent - org.apache.struts.taglib.nested.logic.NestedNotPresentTag - JSP - - Nested Extension - - Generate the nested body content of this tag if the specified - value is not present in this request. -

    - -

    This tag is an extension of the - <logic:notPresent> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - cookie - false - true - - - - header - false - true - - - - name - false - true - - - - parameter - false - true - - - - property - false - true - - - - role - false - true - - - - scope - false - true - - - - user - false - true - - -
    - - present - org.apache.struts.taglib.nested.logic.NestedPresentTag - JSP - - Nested Extension - - Generate the nested body content of this tag if the specified - value is present in this request. -

    - -

    This tag is an extension of the - <logic:present> - tag. Please consult its documentation for information on tag attributes - and usage details. -

    - ]]> -
    - - cookie - false - true - - - - header - false - true - - - - name - false - true - - - - parameter - false - true - - - - property - false - true - - - - role - false - true - - - - scope - false - true - - - - user - false - true - - -
    -
    - - - diff --git a/oeqPrimaryB2/src/main/webapp/taglibs/tle.tld b/oeqPrimaryB2/src/main/webapp/taglibs/tle.tld deleted file mode 100644 index 31e219f..0000000 --- a/oeqPrimaryB2/src/main/webapp/taglibs/tle.tld +++ /dev/null @@ -1,15 +0,0 @@ - - - - 1.0 - 1.1 - The Learning Edge Data Tag Library - /tle - TLE Taglib - - context - org.apereo.openequella.integration.blackboard.buildingblock.ContextTag - JSP - - - From 8c2fb0f289f1d2b227a7d0bceecbba5e188e8e29 Mon Sep 17 00:00:00 2001 From: C Beach Date: Fri, 1 Mar 2019 17:17:23 -0600 Subject: [PATCH 12/14] #4 cleaned up the versioning, fixed changing the link name in the selection session --- .../common/content/ContentUtil.java | 1 - .../buildingblock/Configuration.java | 32 +------------------ .../buildingblock/servlet/ContentServlet.java | 4 +-- oeqPrimaryB2/src/main/webapp/admin/config.jsp | 7 +--- 4 files changed, 4 insertions(+), 40 deletions(-) diff --git a/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/content/ContentUtil.java b/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/content/ContentUtil.java index 8cf7036..3dcbb8c 100644 --- a/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/content/ContentUtil.java +++ b/oeqCommon/src/main/java/org/apereo/openequella/integration/blackboard/common/content/ContentUtil.java @@ -96,7 +96,6 @@ public Content addContent(Course course, Id folderId, String uuid, int version, content.setCourseId(course.getId()); content.setParentId(folderId); content.setIsAvailable(true); - content.setLaunchInNewWindow(true); content.setContentHandler(BbUtil.CONTENT_HANDLER); content.setPosition(-1); content.setIsSequential(false); diff --git a/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/Configuration.java b/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/Configuration.java index 4922eee..242f445 100644 --- a/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/Configuration.java +++ b/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/Configuration.java @@ -86,8 +86,6 @@ public class Configuration { private ContentHandler contentHandler; private final Object contentHandlerLock = new Object(); - /* @Nullable */ - private String version; /* @Nullable */ private String equellaUrl; /* @Nullable */ @@ -129,9 +127,6 @@ private Configuration() { final VirtualHost vhost = vim.getVirtualHost(""); context.getContextManager().setContext(vhost); - version = loadVersion(); - BbLogger.instance().logTrace("Version: " + version); - load(); ensureLtiPlacement(); ensureScoreProvider(); @@ -481,26 +476,6 @@ private ContentHandler getContentHandler() throws Exception { return contentHandler; } - public static String loadVersion() throws IOException { - InputStream in = null; - try { - // CBEACH: This file wasn't able to be read. maybe a permissions or location - // issue. - // final Properties p = new Properties(); - // in = Configuration.class.getResourceAsStream("/version.properties"); - // p.load(in); - // final String versionB2 = p.getProperty("version.b2"); - // return versionB2; - BbLogger.instance().logTrace("Loading Version."); - return "TODO - need to implement version from file system property."; - } catch (Exception e) { - BbLogger.instance().logError("Couldn't load version", e); - throw new RuntimeException(e); - } finally { - Closeables.close(in, false); - } - } - public File getConfigDirectory() { return configDirectory; } @@ -599,11 +574,6 @@ public void setOauthClientSecret(/* @Nullable */String oauthClientSecret) { this.oauthClientSecret = oauthClientSecret; } - /* @Nullable */ - public String getVersion() { - return version; - } - public void setMockPortalRoles(/* @Nullable */Set mockPortalRoles) { this.mockPortalRoles = mockPortalRoles; } @@ -647,4 +617,4 @@ public void setNewWindow(boolean newWindow) { public boolean isNewWindow() { return newWindow; } -} \ No newline at end of file +} diff --git a/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/servlet/ContentServlet.java b/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/servlet/ContentServlet.java index 8afb1cc..bd2231f 100644 --- a/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/servlet/ContentServlet.java +++ b/oeqPrimaryB2/src/main/java/org/apereo/openequella/integration/blackboard/buildingblock/servlet/ContentServlet.java @@ -404,7 +404,7 @@ private void addContent(HttpServletRequest request, HttpServletResponse response while (nodeIter.hasNext()) { final ObjectNode link = (ObjectNode) nodeIter.next(); final String title = nodeValue(link, "name", ""); - final String itemName = nodeValue(link, "itemName", title); + final String itemName = nodeValue(link, "name", title); final String itemDescription = nodeValue(link, "itemDescription", ""); final String description = nodeValue(link, "description", itemDescription); @@ -498,4 +498,4 @@ private void handleSubFolders(ObjectNode parentFolderNode, Content parentFolder, parentFolderNode.put("folders", foldersArray); } } -} \ No newline at end of file +} diff --git a/oeqPrimaryB2/src/main/webapp/admin/config.jsp b/oeqPrimaryB2/src/main/webapp/admin/config.jsp index e91ee76..2227335 100644 --- a/oeqPrimaryB2/src/main/webapp/admin/config.jsp +++ b/oeqPrimaryB2/src/main/webapp/admin/config.jsp @@ -84,7 +84,6 @@ catch(Exception e) configuration.load(); String equellaurl = configuration.getEquellaUrl(); -String version = configuration.getVersion(); String clientId = configuration.getOauthClientId(); String clientSecret = configuration.getOauthClientSecret(); String secretId = configuration.getSecretId(); @@ -172,11 +171,7 @@ int number = 1; - -
    - <%=version%> -
    - +
    Logging: <%=loggingDetails%>
    From 4f3007a1fc85559c882794d2a4c9dbdf10ab4df2 Mon Sep 17 00:00:00 2001 From: C Beach Date: Fri, 1 Mar 2019 17:19:11 -0600 Subject: [PATCH 13/14] #4 Releasing version 2.0.1 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index d3de13f..8a54a37 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,5 +6,5 @@ jspApiVersion=2.3.3 servletApiVersion=4.0.1 junitVersion=4.12 # All building blocks / web services are re-versioned when this changes. -artifactVersion=2.0.1-SNAPSHOT +artifactVersion=2.0.1 From 2f1a523ee8446b731acd715290aa654a9968677a Mon Sep 17 00:00:00 2001 From: C Beach Date: Tue, 5 Mar 2019 13:17:15 -0600 Subject: [PATCH 14/14] #4 Removing IDE specific files --- .gitignore | 2 +- .vscode/settings.json | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.gitignore b/.gitignore index 1cd72ba..d9c17bc 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,4 @@ build/ .settings/ .project .classpath - +.vscode/ diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 3500a66..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "java.configuration.updateBuildConfiguration": "interactive", - "files.exclude": { - "**/.classpath": true, - "**/.project": true, - "**/.settings": true, - "**/.factorypath": true - } -} \ No newline at end of file