diff --git a/ReadMe.txt b/ReadMe.txt new file mode 100644 index 0000000000..4fb0037ec4 --- /dev/null +++ b/ReadMe.txt @@ -0,0 +1,53 @@ +== Alfresco Records Management - Development Environment Setup == + +Prerequisites + + - Gradle 1.0 milestone 8a (optional unless native gradle support is required) + - Alfresco Repository 4.0.1 (or higher). Specifically you need the alfresco.war and share.war files. + - Any prerequisties required for an Alfresco installation, including Java 1.5, Tomcat and a suitable database. + + NOTE: throughout these instructions we will use the 'gradlew' wrapper command. This wrapper command downloads the required Gradle libraries automatically. Should you prefer to use the native 'gradle' command instead you will need to install Gradle manually. + +Initial Setup + + - Checkout Records Management code from https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/HEAD + Note: the RM branch structure reflects the main repository branch structure, so HEAD is the current development branch and BRACHES contains the release branches. + - Place the alfresco.war in the root of the rm-server directory and place the share.war in the root of the rm-share directory. + Note: building BRACHES/V4.0 will provide compatiable wars, alternatively a compatible build can be retrieved from bamboo.alfresco.com + - Run "gradlew amp" in root directory. This will unpack the dependancies, build the RM source and finally assemble the RM amps. + Note: the first execution of this task may take serveral minutes, because it will unpack the required build dependancies from the alfresco and share WAR. It will also pull any external dependancies from Maven or + - You will not find rm-server\build\dist\alfresco-rm-2.0.amp and rm-share\build\dist\alfresco-rm-share-2.0.amp ahve been built. + +Using Eclipse + + - Start Eclipse in the usual way. + Note: make sure the WAR dependancies have been exploded before opening Eclispe. + - Import projects found in rm-server and rm-share directories. + +Deploying the RM AMPs + + - Set the envoronment variables TOMCAT_HOME and APP_TOMCAT_HOME to the home directory of the repository and share Tomcat instances respectively. + NOTE: these can be the same tomcat instance, but it is recommended that two are used. + - Configure your repository Tomcat so that your repository.properties settings will be successfully picked up when Alfresco is started. + - Run "gradlew installAmp" in the root directory. This will use the MMT to apply the RM AMPs to the Alfresco and Share WARs respectively. The modified WARs will then be copied to the set Tomcat instances, cleaning any exisiting exploded WARs. + - Start Tomcat(s). + +For users of the Alfresco DevEnv + + - Create a normal project using "create-project". + - Manually check out RM code into the "code" directory as described above. Don't use the checkout script provided. + - The devEnv will automatically set the TOMCAT_HOME and APP_TOMCAT_HOME environment variables to point to the Tomcat instances created by the use-tomcat6 and use-app-tomcat6 scipts. Magic! + - You can use the dev-context.xml generated for you to configure the repository. Place it in /shared/alfresco/extension. + +Summary Of Available Gradle Tasks + + Note: All these tasks can be executed in the root directory or in either of the sub-project directories. + Note: Use the command "gradle " when executing. + Note: The RM Gradle scripts import the standard "Java" package so those associated standard tasks are available, for example "jar", "compileJava", "clean", etc + + - explodeDeps : checks for existance of the projects dependant WAR (either alfresco.war or share.war). If not already exploded, unpacks the required depedancies from the WAR files. + - cleanDeps : cleans the projects exploded dependancies. + - amp : builds the projects AMP and places it in build/dist. + - installAmp : installs the AMP into a copy of the projects dependant WAR using the MMT. + NOTE: the installed WAR can be found in build/dist. + - deployAmp : depolys the project AMP to the configured Tomcat instance. \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000000..fb03a8f61a --- /dev/null +++ b/build.gradle @@ -0,0 +1,293 @@ +buildscript { + + repositories { + flatDir { + dirs 'mmt' + } + } + + dependencies { + classpath fileTree(dir: 'mmt', include: '*.jar') + } +} + +task wrapper(type: Wrapper) { + gradleVersion = '1.0-milestone-8' +} + +task packageBuild (dependsOn: [':rm-server:amp', ':rm-share:amp']) << { + + distDir = file('dist') + if (distDir.exists() == false) { + distDir.mkdirs(); + } + + packageBaseName = "${groupid}-${packageName}-${version}-${build}" + packageZipFile = "${packageBaseName}.zip" + alfrescoAmp = "${project(':rm-server').name}/${project(':rm-server').buildDistDir}/${project(':rm-server').ampFile}" + shareAmp = "${project(':rm-share').name}/${project(':rm-share').buildDistDir}/${project(':rm-share').ampFile}" + + ant.zip(destfile: "${distDir}/${packageZipFile}", update: 'true') { + + ant.zipfileset(file: "${alfrescoAmp}") + ant.zipfileset(file: "${shareAmp}") + } +} + +/** Subproject configuration */ +subprojects { + + apply plugin: 'java' + + sourceCompatibility = 1.6 + targetCompatibility = 1.6 + + explodedDepsDir = 'explodedDeps' + explodedLibsDir = "${explodedDepsDir}/lib" + explodedConfigDir = "${explodedDepsDir}/config" + buildDistDir = 'build/dist' + buildLibDir = 'build/libs' + sourceJavaDir = 'source/java' + sourceWebDir = 'source/web' + testJavaDir = 'test/java' + testResourceDir = 'test/resource' + configDir = 'config' + configModuleDir = "config/alfresco/module/${moduleid}" + moduleProperties = 'module.properties' + fileMapping = 'file-mapping.properties' + baseName = "${groupid}-${appName}-${version}-${build}" + jarFile = "${baseName}.jar" + ampFile = "${baseName}.amp" + tomcatRoot = System.getenv(tomcatEnv) + jarFilePath = "${buildLibDir}/${jarFile}" + + sourceSets { + main { + java { + srcDir sourceJavaDir + } + } + test { + java { + srcDir testJavaDir + } + resources { + srcDir testResourceDir + } + } + } + + repositories { + + flatDir { + dirs explodedLibsDir + } + mavenCentral() + } + + dependencies { + + compile fileTree(dir: explodedLibsDir, include: '*.jar') + } + + /** --- Compile tasks --- */ + + // make sure that the dependancies have been unpacked before compiling the Java + compileJava.doFirst { + explodeDeps.execute() + } + + jar.archiveName = jarFile + + /** --- Dependancy tasks --- */ + + task explodeDeps << { + + explodedDir = file(explodedDepsDir) + explodedLibDir = file(explodedLibsDir) + explodedConfigDir = file("${explodedDepsDir}/config") + warFileObj = file(warFile) + + if (warFileObj.exists() == true) { + + logger.lifecycle "${warFile} was found. Checking dependancies ..." + + if (explodedDir.exists() == false) { + println(" ... creating destination dir ${explodedDir}") + explodedDir.mkdir() + } + + if (isUnpacked(explodedLibDir) == false) { + + println(" ... unpacking libs into ${explodedLibDir}") + + ant.unzip(src: warFileObj, dest: explodedLibDir) { + ant.patternset { + ant.include(name: 'WEB-INF/lib/*.jar') + } + ant.mapper(type: 'flatten') + } + } + + if (isUnpacked(explodedConfigDir) == false) { + + println(" ... unpacking config into ${explodedConfigDir}") + + ant.unzip(src: warFileObj, dest: explodedDir) { + ant.patternset { + ant.include(name: 'WEB-INF/classes/**/*') + } + } + + copy { + from "${explodedDir}/WEB-INF/classes" + into explodedConfigDir + } + + // TODO understand why this doesn't delete the folder as expected + ant.delete(includeEmptyDirs: 'true') { + ant.fileset(dir: "${explodedDir}/WEB-INF", includes: '**/*') + } + } + } + else { + logger.error "Dependant WAR file ${warFile} can not be found. Please place it in ${warFileObj.getPath()} to continue." + throw new TaskInstantiationException("Dependant WAR file ${warFile} can not be found. Please place it in ${warFileObj.getPath()} to continue.") + } + } + + task cleanDeps << { + ant.delete(includeEmptyDirs: 'true') { + ant.fileset(dir: explodedDepsDir, includes: '**/*') + } + } + + /** --- AMP tasks --- */ + + task copyWar(type: Copy) { + from warFile + into buildDistDir + } + + task amp(dependsOn: 'jar') << { + + def jarFileObj = file(jarFilePath) + def configDirObj = file(configDir) + def sourceWebObj = file(sourceWebDir) + + // assemble the AMP file + ant.zip(destfile: "${buildDistDir}/${ampFile}", update: 'true') { + + ant.zipfileset(file: "${configModuleDir}/${moduleProperties}") + ant.zipfileset(file: "${configModuleDir}/${fileMapping}") + + if (jarFileObj.exists()) { + + logger.info("Adding ${jarFilePath} to ${ampFile} in /lib") + ant.zipfileset(file: jarFilePath, prefix: 'lib') + } + + if (configDirObj.exists() == true) { + + logger.info("Adding ${configDir} to ${ampFile} in /config") + + ant.zipfileset(dir: configDir, prefix: 'config') { + ant.exclude(name: '**/' + moduleProperties) + ant.exclude(name: '**/' + fileMapping) + } + } + + if (sourceWebObj.exists() == true) { + + logger.info("Adding ${sourceWebDir} to ${ampFile} in /web") + + ant.zipfileset(dir: sourceWebDir, prefix: 'web') + } + } + } + + task deployExploded(dependsOn: 'jar') << { + + def jarFileObj = file(jarFilePath) + def configDirObj = file(configDir) + def sourceWebObj = file(sourceWebDir) + + explodedWebAppDir = new File("${tomcatRoot}/webapps/${webAppName}") + if (explodedWebAppDir.exists() == true) { + + // copy module properties + // TODO but not so important for now + + // copy jars + if (jarFileObj.exists()) { + copy { + from jarFilePath + into "${explodedWebAppDir}/WEB-INF/lib" + } + } + + // copy config + if (configDirObj.exists() == true) { + copy { + from(configDir) { + exclude "**/${moduleProperties}" + exclude "**/${fileMapping}" + } + into "${explodedWebAppDir}/WEB-INF/classes" + + } + } + + // copy web + if (sourceWebObj.exists() == true) { + copy { + from sourceWebObj + into "${explodedWebAppDir}" + } + } + } + else { + println "Exploded webapp directory ${explodedWebAppDir} does not exist." + } + } + + task installAmp(dependsOn: ['amp', 'copyWar']) << { + + def warFileLocation = file("${buildDistDir}/${warFile}") + def ampFileLocation = file("${buildDistDir}/${ampFile}") + + mmt = new org.alfresco.repo.module.tool.ModuleManagementTool() + mmt.setVerbose(true) + mmt.installModule(ampFileLocation.getPath(), warFileLocation.getPath(), false, true, false) + } + + task cleanDeploy(type: Delete) { + delete "${tomcatRoot}/webapps/${webAppName}", "${tomcatRoot}/webapps/${warFile}" + } + + task deployAmp(dependsOn: ['cleanDeploy', 'installAmp']) << { + + tomcatRootDir = new File(tomcatRoot) + if (tomcatRootDir.exists() == true) { + + // copy war + copy { + from "${buildDistDir}/${warFile}" + into "${tomcatRoot}/webapps" + } + } + else { + println "Tomcat root directory ${tomcatRoot} does not exist." + } + } +} + +/** Utility function - indicates wether the provided dir is unpacked (ie exists and has some contents) */ +Boolean isUnpacked(dir) { + if (dir.exists() == true && dir.list().length > 0) { + return true + } + else { + return false + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000000..a9ec5c46df --- /dev/null +++ b/gradle.properties @@ -0,0 +1,4 @@ +groupid=alfresco +packageName=rm +version=2.0.0 +build=1 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000..5437731f7e Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..897e71b9e1 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Apr 04 12:27:15 EST 2012 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=http\://services.gradle.org/distributions/gradle-1.0-milestone-8-bin.zip diff --git a/gradlew b/gradlew new file mode 100644 index 0000000000..ae91ed9029 --- /dev/null +++ b/gradlew @@ -0,0 +1,164 @@ +#!/bin/bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" +APP_HOME="`pwd -P`" +cd "$SAVED" + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query businessSystem maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + JAVA_OPTS="$JAVA_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000000..aec99730b4 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/mmt/alfresco-mmt-main.jar b/mmt/alfresco-mmt-main.jar new file mode 100644 index 0000000000..d395dfa05d Binary files /dev/null and b/mmt/alfresco-mmt-main.jar differ diff --git a/mmt/jug-asl-2.0.0.jar b/mmt/jug-asl-2.0.0.jar new file mode 100644 index 0000000000..c86a68f97b Binary files /dev/null and b/mmt/jug-asl-2.0.0.jar differ diff --git a/mmt/truezip.jar b/mmt/truezip.jar new file mode 100644 index 0000000000..fe6cd1ea00 Binary files /dev/null and b/mmt/truezip.jar differ diff --git a/rm-server/.classpath b/rm-server/.classpath new file mode 100644 index 0000000000..e2ff39fa0f --- /dev/null +++ b/rm-server/.classpath @@ -0,0 +1,283 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rm-server/.project b/rm-server/.project new file mode 100644 index 0000000000..47a2d689a4 --- /dev/null +++ b/rm-server/.project @@ -0,0 +1,16 @@ + + + rm-server + + + + org.eclipse.jdt.core.javanature + + + + org.eclipse.jdt.core.javabuilder + + + + + diff --git a/rm-server/.settings/org.eclipse.jdt.core.prefs b/rm-server/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000000..1d946df44c --- /dev/null +++ b/rm-server/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,12 @@ +#Fri Mar 23 15:41:37 EST 2012 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/rm-server/build.gradle b/rm-server/build.gradle new file mode 100644 index 0000000000..0008684d53 --- /dev/null +++ b/rm-server/build.gradle @@ -0,0 +1,23 @@ +dependencies { + + compile fileTree(dir: 'libs', include: '*.jar') + compile 'javax.servlet:servlet-api:2.5' + + testCompile 'org.springframework:spring-test:2.5' + + testRuntime files(explodedConfigDir) + testRuntime files(configDir) +} + +test { + //makes the standard streams (err and out) visible at console when running tests + testLogging.showStandardStreams = true + + //tweaking memory settings for the forked vm that runs tests + jvmArgs '-Xms256M', '-Xmx1024M', '-XX:MaxPermSize=256M' + + //listening to test execution events + beforeTest { descriptor -> + logger.lifecycle("Running test: " + descriptor) + } +} \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/alfresco-global.properties b/rm-server/config/alfresco/module/org_alfresco_module_rm/alfresco-global.properties new file mode 100644 index 0000000000..ffbe1a1259 --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/alfresco-global.properties @@ -0,0 +1,19 @@ +# Enable ghosting of records on deletion +rm.ghosting.enabled=true + +# Notification configuration +rm.notification.role=RecordsManager +# NOTE: the notification subject can now be set within the usual I18N property files per notification template + +# +# Turn off imap server attachments if we are using RM. +# TODO : Longer term needs to have a query based, dynamic +# exclusion for RM sites. +# +imap.server.attachments.extraction.enabled=false + +# +# Enable auditing +# +audit.enabled=true +audit.rm.enabled=true \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/audit/rm-audit.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/audit/rm-audit.xml new file mode 100644 index 0000000000..6ceab3c551 --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/audit/rm-audit.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/bootstrap/RMDataDictionaryBootstrap.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/bootstrap/RMDataDictionaryBootstrap.xml new file mode 100644 index 0000000000..57ee9dac21 --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/bootstrap/RMDataDictionaryBootstrap.xml @@ -0,0 +1,177 @@ + + + + + + + workspace + SpacesStore + rm_config_folder + Records Management + Records Management + Configuration information for the Records Management application. + + + + + + + + + + + workspace + SpacesStore + rm_event_config + Records management event configuration. + contentUrl=classpath:alfresco/module/org_alfresco_module_rm/bootstrap/content/rmEventConfigBootstrap.json|mimetype=text/plain|encoding=UTF-8 + rm_event_config.json + rm_event_config.json + + + + + + + + + workspace + SpacesStore + records_management_custom_model + Records Management Custom Model + contentUrl=classpath:alfresco/module/org_alfresco_module_rm/bootstrap/content/recordsCustomModel.xml|mimetype=text/plain|encoding=UTF-8 + recordsCustomModel.xml + recordsCustomModel.xml + {http://www.alfresco.org/model/rmcustom/1.0}rmc + Records Management Custom Model + Alfresco + 1.0 + true + + + + + + + workspace + SpacesStore + rm_scripts + Records Management Scripts + Records Management Scripts + Scripts intended for execution in response to RM events. + + + + + + + + + + workspace + SpacesStore + Records management sample script. + contentUrl=classpath:alfresco/module/org_alfresco_module_rm/bootstrap/content/rma_isClosed.js|mimetype=text/javascript|encoding=UTF-8 + rma_isClosed.js + rma_isClosed.js + + + + + + + + + workspace + SpacesStore + Records management sample script. + contentUrl=classpath:alfresco/module/org_alfresco_module_rm/bootstrap/content/onCreate_supersedes.js|mimetype=text/javascript|encoding=UTF-8 + onCreate_supersedes.js + onCreate_supersedes.js + + + + + + + + + + workspace + SpacesStore + records_management_email_templates + Records Management Email Templates + Records Management Email Templates + Email templates for records management. + + + + + + + + + + + + + + true + + Email template for notify records due for review job. + contentUrl=classpath:alfresco/module/org_alfresco_module_rm/bootstrap/content/notify-records-due-for-review-email.ftl|mimetype=text/plain|size=|encoding=UTF-8|locale=en_US_ + notify-records-due-for-review-email.ftl + + notify-records-due-for-review-email.ftl + org_alfresco_module_rm_notificationTemplatePatch + + + + + + + + + + + + workspace + SpacesStore + record_superseded_template + Record superseded email template. + contentUrl=classpath:alfresco/module/org_alfresco_module_rm/bootstrap/content/record-superseded-email.ftl|mimetype=text/plain|encoding=UTF-8 + record-superseded-email.ftl + record-superseded-email.ftl + org_alfresco_module_rm_notificationTemplatePatch + + + + + + + + + + + \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/bootstrap/content/notify-records-due-for-review-email.ftl b/rm-server/config/alfresco/module/org_alfresco_module_rm/bootstrap/content/notify-records-due-for-review-email.ftl new file mode 100644 index 0000000000..acf23fce72 --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/bootstrap/content/notify-records-due-for-review-email.ftl @@ -0,0 +1,124 @@ + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ + +
+ Records due for review. +
+
+ ${date?datetime?string.full} +
+
+
+

Hi,

+ +

The following records are now due for review:

+ + <#if (args.records)??> + + <#list args.records as record> + + + + <#if record_has_next> + + + +
+ + + + + +
+ + + + + + + + + + + + +
${record.properties["rma:identifier"]!} ${record.name}
Click on this link to view the record:
+ + ${shareUrl}/page/site/${args.site}/document-details?nodeRef=${record.storeType}://${record.storeId}/${record.id} +
+
+
+ + +

Sincerely,
+ Alfresco ${productName!""}

+
+
+
+
 
+
+ To find out more about Alfresco ${productName!""} visit http://www.alfresco.com +
+
 
+
+ +
+
+
+ + \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/bootstrap/content/onCreate_supersedes.js b/rm-server/config/alfresco/module/org_alfresco_module_rm/bootstrap/content/onCreate_supersedes.js new file mode 100644 index 0000000000..330a794f50 --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/bootstrap/content/onCreate_supersedes.js @@ -0,0 +1,15 @@ +/** + * Main entrypoint for script. + * + * @method main + */ +function main() +{ + // Log debug message + logger.log("Record " + node.name + " has been superseded. Sending notification"); + + // Send notification + rmService.sendSupersededNotification(node); +} + +main(); diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/bootstrap/content/record-superseded-email.ftl b/rm-server/config/alfresco/module/org_alfresco_module_rm/bootstrap/content/record-superseded-email.ftl new file mode 100644 index 0000000000..e15b1024f3 --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/bootstrap/content/record-superseded-email.ftl @@ -0,0 +1,118 @@ + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ + +
+ Superseded record. +
+
+ ${date?datetime?string.full} +
+
+
+

Hi,

+ +

The following record been superseded:

+ + + + + + +
+ + + + + +
+ + + + + + + + + + + + +
${args.record.properties["rma:identifier"]!} ${args.record.name}
Click on this link to view the record:
+ + ${shareUrl}/page/site/${args.site}/document-details?nodeRef=${args.record.storeType}://${args.record.storeId}/${args.record.id} +
+
+
+ +

Sincerely,
+ Alfresco ${productName!""}

+
+
+
+
 
+
+ To find out more about Alfresco ${productName!""} visit http://www.alfresco.com +
+
 
+
+ +
+
+
+ + diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/bootstrap/content/recordsCustomModel.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/bootstrap/content/recordsCustomModel.xml new file mode 100644 index 0000000000..46f9d6b45c --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/bootstrap/content/recordsCustomModel.xml @@ -0,0 +1,159 @@ + + + + + + + + + Records Management Custom Model + Alfresco + 1.0 + + + + + + + + + + + + + + + + + + + + + + + Supplemental Markings + + + + + true + + + + + Transfer Locations + + + + + true + + + + + + + + + + Supplemental Marking List + d:text + false + true + + + + + + + + + + Records Management Custom Associations + + + + + SupersededBy__Supersedes + + false + true + + + rma:record + false + true + + + + + ObsoletedBy__Obsoletes + + false + true + + + rma:record + false + true + + + + + VersionedBy__Versions + + false + true + + + rma:record + false + true + + + + + Supporting Documentation__Supported Documentation + + false + true + + + rma:record + false + true + + + + + Cross-Reference + + false + true + + + rma:record + false + true + + + + + Rendition + + false + true + + + rma:record + false + true + + + + + + + + + \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/bootstrap/content/rmEventConfigBootstrap.json b/rm-server/config/alfresco/module/org_alfresco_module_rm/bootstrap/content/rmEventConfigBootstrap.json new file mode 100644 index 0000000000..07cab10ffa --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/bootstrap/content/rmEventConfigBootstrap.json @@ -0,0 +1,71 @@ +{ + "events" : + [ + { + "eventType" : "rmEventType.simple", + "eventName" : "case_closed", + "eventDisplayLabel" : "Case Closed" + }, + { + "eventType" : "rmEventType.simple", + "eventName" : "abolished", + "eventDisplayLabel" : "Abolished" + }, + { + "eventType" : "rmEventType.simple", + "eventName" : "re_designated", + "eventDisplayLabel" : "Redesignated" + }, + { + "eventType" : "rmEventType.simple", + "eventName" : "no_longer_needed", + "eventDisplayLabel" : "No longer needed" + }, + { + "eventType" : "rmEventType.superseded", + "eventName" : "superseded", + "eventDisplayLabel" : "Superseded" + }, + { + "eventType" : "rmEventType.simple", + "eventName" : "study_complete", + "eventDisplayLabel" : "Study Complete" + }, + { + "eventType" : "rmEventType.simple", + "eventName" : "training_complete", + "eventDisplayLabel" : "Training Complete" + }, + { + "eventType" : "rmEventType.crossReferencedRecordTransfered", + "eventName" : "related_record_trasfered_inactive_storage", + "eventDisplayLabel" : "Related Record Transfered To Inactive Storage" + }, + { + "eventType" : "rmEventType.obsolete", + "eventName" : "obsolete", + "eventDisplayLabel" : "Obsolete" + }, + { + "eventType" : "rmEventType.simple", + "eventName" : "all_allowances_granted_are_terminated", + "eventDisplayLabel" : "All Allowances Granted Are Terminated" + }, + { + "eventType" : "rmEventType.simple", + "eventName" : "WGI_action_complete", + "eventDisplayLabel" : "WGI action complete" + }, + { + "eventType" : "rmEventType.simple", + "eventName" : "separation", + "eventDisplayLabel" : "Separation" + }, + { + "eventType" : "rmEventType.simple", + "eventName" : "case_complete", + "eventDisplayLabel" : "Case Complete" + } + ] +} + diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/bootstrap/content/rma_isClosed.js b/rm-server/config/alfresco/module/org_alfresco_module_rm/bootstrap/content/rma_isClosed.js new file mode 100644 index 0000000000..1728d5c903 --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/bootstrap/content/rma_isClosed.js @@ -0,0 +1,12 @@ +/** + * Main entrypoint for script. + * This sample script simply echoes the name of the node with the changed property. + * + * @method main + */ +function main() +{ + logger.log("Sample RM script. No-op run on node " + node.name); +} + +main(); diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/dod5015/DODExampleFilePlan.acp b/rm-server/config/alfresco/module/org_alfresco_module_rm/dod5015/DODExampleFilePlan.acp new file mode 100644 index 0000000000..a7d23b0890 Binary files /dev/null and b/rm-server/config/alfresco/module/org_alfresco_module_rm/dod5015/DODExampleFilePlan.acp differ diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/dod5015/DODExampleFilePlan.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/dod5015/DODExampleFilePlan.xml new file mode 100644 index 0000000000..9b40dfe679 --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/dod5015/DODExampleFilePlan.xml @@ -0,0 +1,1046 @@ + + + + + + + Reports + 0318 + Reports + Record series for reports + + + + + + + + + + + + + AIS Audit Records + 0318-01 + AIS Audit Records + Consisting of AIS Security Officer or Terminal Area Security Officer weekly audit records of audit actions performed on all AIS as required by applicable policy which are maintained by any JS/combatant command activity. + week|1 + true + + + + + + + + + N1-218-00-4 item 023 + Cut off monthly, hold 1 month, then destroy. + + + + + + + + cutoff + monthend|1 + + + + + destroy + month|1 + {http://www.alfresco.org/model/recordsmanagement/1.0}cutOffDate + + + + + + + + + + + + + + January AIS Audit Records + January AIS Audit Records + 0318-01-01 + week|1 + true + + + + + + + + + + + + + + + Unit Manning Documents + 0318-02 + Unit Manning Documents + Consisting of manpower document and monthly strength report forwarded to OSD and other activities which are maintained by personnel office as the official record copy. + + + + + + + + + N1-218-89-1 item 002 + Cut off every 3 months, hold 3 months, then destroy. + + + + + + + + cutoff + quarterend|1 + + + + + destroy + month|3 + {http://www.alfresco.org/model/recordsmanagement/1.0}cutOffDate + + + + + + + + + + + + + 1st Quarter Unit Manning Documents + 0318-02-01 + 1st Quarter Unit Manning Documents + + + + + + + + + + + + + + + Overtime Reports + 0318-03 + Overtime reports and related documents + Overtime reports and related documents which are maintained by JS/combatant controller as the official record copy. + + + + + + + + + N1-218-00-7 item 28 + Cut off at end of FY, hold 3 years, then destroy. + + + + + + + cutoff + fyend|1 + + + + + destroy + year|3 + {http://www.alfresco.org/model/recordsmanagement/1.0}cutOffDate + + + + + + + + + + + + FY08 Overtime Reports + 0318-03-01 + FY08 Overtime Reports + + + + + + + + + + + + + + + Bi-Weekly Cost Reports + 0318-04 + Bi-Weekly Cost Reports + Bi-wekly cost reports which are maintained by JS/combatant command controler as the official record copy. + + + + + + + + + N1-218-00-7 item 2 + Cut off at end of CY, hold 2 years, then destroy. + + + + + + + cutoff + yearend|1 + + + + + destroy + year|2 + {http://www.alfresco.org/model/recordsmanagement/1.0}cutOffDate + + + + + + + + + + + CY08 Unit Manning Documents + 0318-04-01 + CY08 Unit Manning Documents + + + + + + + + + + + + + + + + Military Files + 0412 + Military Files + Record series for military files + + + + + + + + + + + + + Military Assignment Documents + 0412-01 + Military Assignment Documents + Policy matters pertaining to military assignments which are maintained by any JS/combatant command activity as the official record copy. + + + + + + + + + N1-218-00-3 item 30 + Cut off when superseded, hold 5 years, then destroy. + true + + + + + + + cutoff + superseded + + + + + destroy + year|5 + {http://www.alfresco.org/model/recordsmanagement/1.0}cutOffDate + + + + + + + + + + + + + + + + + + + + + Official Military Personnel Privilege Card Applications + 0412-02 + Official Military Personnel Privilege Card Applications + Consisting of: documents reflecting applications for priviege cards and ration cards, including Department of Defense Forms (DD Forms) 1172 (Application for Unifomed Services Identification and Privilege Card) and similar documents which are maintained by any JS/combatant command activity. + + + + + + + + + N1-218-00-3 item 20 + Cut off when no longer needed and destroy immediately. + + + + + + + cutoff + no_longer_needed + + + + + destroy + immediately|0 + + + + + + + + + + + + COL Bob Johnson + 0412-02-01 + COL Bob Johnson + + + + + PFC Alan Murphy + 0412-02-02 + PFC Alan Murphy + + + + + + + + + + + + + + + Personnel Security Program Records + 0412-03 + Personnel Security Program Records + Position sensitivity files including requests for information relating to the designation of sensitive and non-sensitive personnel positions in an agency and results of final actions taken consisting of approved requests which are maintained by any JS/combatant command activity + + + + + + + + + N1-218-00-4 item 017 + Cutoff when position is abolished, re-designated, or no longer needed, whichever is later. Destroy immediately after cutoff. + + + + + + + cutoff + + abolished + re_designated + no_longer_needed + + and + + + + + destroy + immediately|0 + + + + + + + + + + + + Commander's Administrative Assistant + 0412-03-01 + Commander's Administrative Assistant + + + + + Equal Opportunity Coordinator + 0412-03-02 + Equal Opportunity Coordinator + + + + + + + + + + + + + + + + Civilian Files + 0430 + Civilian Files + Record series for civilian files + + + + + + + + + + + + + Employee Performance File System Records + 0430-01 + Employee Performance File System Records + Consisting of: performance records superseded through an administrative, judicial, or quasi-judicial procedure which are maintained by any JS/combatant command activity + + + + + + + + + GRS 1 item 23b(1) + Cutoff when superseded. Destroy immediately after cutoff + true + + + + + + + cutoff + superseded + + + + + destroy + immediately|0 + + + + + + + + + + + + + + + + + + + Foreign Employee Award Files + 0430-02 + Foreign Employee Award Files + Decorations to foreign nationals and US citizens not employed by the US Government consisting of: case files of recommendations, decisions, awards announcements, board meeting minutes, and related documents which are maintained by any JS/combatant command activity + + + + + + + + N1-218-00-3 item 18 + Permanent. Cut off on completion of case, hold 2 years, then retire to offline storage. Transfer to federal records holding area 5 years after retirement to offline storage. Transfer to NARA 25 years after cutoff. + + + + + + + cutoff + case_complete + + + + + transfer + Retire to offline storage. + Offline Storage + year|2 + {http://www.alfresco.org/model/recordsmanagement/1.0}cutOffDate + + + + + transfer + Transfer to federal records holding area. + Federal Records Holding + year|5 + {http://www.alfresco.org/model/recordsmanagement/1.0}dispositionAsOf + + + + + accession + Transfer to NARA. + NARA + year|25 + {http://www.alfresco.org/model/recordsmanagement/1.0}cutOffDate + + + + + + + + + + + + Christian Bohr + 0430-02-01 + Christian Bohr + + + + + Karl Planck + 0430-02-02 + Karl Planck + + + + + + + + + + + + + + + Case Files and Papers + 0430-03 + Case Files and Papers + Consisting of library containing information on personnel actions which are maintained by R&A Br and Deputy Chief Information Office + None + Disposal not authorized. Disposition pending NARA approval. + + + + + + + Gilbert Competency Hearing + 0430-03-01 + Gilbert Competency Hearing + + + + + + + + + + + + + + + Withholding of Within-Grade Increase (WGI) Records + 0430-04 + Withholding of Within-Grade Increase (WGI) Records + Files concerning an employee’s performance rating of record with work examples which establish less than fully successful performance, notice of withholding of WGI, employee's request for reconsideration of denied WGI, and decision concerning such a reconsideration request which are maintained by any JS/combatant command activity. + + + + + + + + + N1-218-00-3 item 16 + Cut off on completion of WGI action or on separation, whichever is earlier; hold 3 years, then destroy/delete. + + + + + + + cutoff + + WGI_action_complete + separation + + or + + + + + destroy + year|3 + {http://www.alfresco.org/model/recordsmanagement/1.0}cutOffDate + + + + + + + + + + + + Gilbert WGI Records + 0430-04-01 + Gilbert WGI Records + + + + + + + + + + + + + + + Payroll Differential and Allowances + 0430-05 + Payroll Differential and Allowances + Consisting of: information to assist overseas civilian personnel offices to document employee eligibility for foreign post differential and foreign quarters and post allowances, including SF 1190 (Foreign Allowances Application, Grant, and Report) and similar information which are maintained by any JS/combatant command activity. + + + + + + + + + N1-218-00-3 item 3 + Cut off at end of Fiscal Year (FY) in which all allowances granted are terminated, hold 3 years, then destroy. + + + + + + + retain + all_allowances_granted_are_terminated + + + + + cutoff + fyend|1 + {http://www.alfresco.org/model/recordsmanagement/1.0}dispositionAsOf + + + + + destroy + year|3 + {http://www.alfresco.org/model/recordsmanagement/1.0}cutOffDate + + + + + + + + + + + + Martin Payroll Differential and Allowances + 0430-05-01 + Martin Payroll Differential and Allowances + + + + + + + + + + + + + + + + Miscellaneous Files + 0950 + Miscellaneous Files + Record series for miscellaneous files + + + + + + + + + + + + + Civilian Employee Training Program Records + 0950-01 + Civilian Employee Training Program Records + Decorations to foreign nationals and US citizens not employed by the US Government consisting of: case files of recommendations, decisions, awards announcements, board meeting minutes, and related documents which are maintained by any JS/combatant command activity + + + + + + + + GRS 1 item 29b + Cut off annually, hold 5 years, then destroy, or destroy when obsolete, whichever is earlier. + + + + + + + cutoff + year|1 + {http://www.alfresco.org/model/recordsmanagement/1.0}dateFiled + + + + + destroy + year|5 + {http://www.alfresco.org/model/recordsmanagement/1.0}cutOffDate + obsolete + + + + + + + + + + + + Bob Prentice Training Records (2008) + 0950-01-01 + Bob Prentice Training Records (2008) + + + + + Beth Tanaka Training Records (2008) + 0950-01-02 + Beth Tanaka Training Records (2008) + + + + + Chuck Stevens Training Records (2008) + 0950-01-03 + Chuck Stevens Training Records (2008) + + + + + + + + + + + + + + + Purchase of Foreign Award Medals and Decorations + 0950-02 + Purchase of Foreign Award Medals and Decorations + Forms reflecting purchase of foreign award medals and decorations. + + + + + + + + + N1-218-00-3 item 11 + Cutoff when related record is transferred to inactive storage, hold 1 year, destroy. + true + + + + + + + cutoff + related_record_trasfered_inactive_storage + + + + + destroy + year|1 + {http://www.alfresco.org/model/recordsmanagement/1.0}cutOffDate + + + + + + + + + + + + + + + + + + + + Monthly Cockpit Crew Training + 0950-03 + Monthly Cockpit/Crew Training + Consisting of skills training/evaluation forms, e.g., AF Form 4031. + + + + + + + + + N1-218-00-3 item 13 + Cutoff after training is complete, hold 1 year, destroy. + + + + + + + cutoff + training_complete + + + + + destroy + year|1 + {http://www.alfresco.org/model/recordsmanagement/1.0}cutOffDate + + + + + + + + + + + + January Cockpit Crew Training + 0950-03-01 + January Cockpit/Crew Training + + + + + February Cockpit Crew Training + 0950-03-02 + February Cockpit/Crew Training + + + + + + + + + + + + + + + Science Advisor Records + 0950-04 + Science Advisor Records + Consisting of: reports, studies, tasking orders, and similar records. Reports are usually informal and unpublished. Records may be generated at all activities + + + + + + + + + N1-218-00-10 item 44 + Cut off on completion of study, hold 5 years, then transfer to Inactive Storage. Transfer to NARA 25 years after cutoff. + + + + + + + cutoff + study_complete + + + + + transfer + Transfer to inactive storage. + Inactive Storage + year|5 + {http://www.alfresco.org/model/recordsmanagement/1.0}cutOffDate + + + + + accession + Transfer to NARA. + NARA + year|25 + {http://www.alfresco.org/model/recordsmanagement/1.0}cutOffDate + + + + + + + + + + + + Phoenix Mars Mission + 0950-04-01 + Phoenix Mars Mission + + + + + + + + + + + + + \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/dod5015/dod5015-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/dod5015/dod5015-context.xml new file mode 100644 index 0000000000..bb2d6a5f11 --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/dod5015/dod5015-context.xml @@ -0,0 +1,20 @@ + + + + + + + + + + alfresco/module/org_alfresco_module_rm/dod5015/dod5015Model.xml + + + + + alfresco/module/org_alfresco_module_rm/dod5015/dod5015-model + + + + + \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/dod5015/dod5015-model.properties b/rm-server/config/alfresco/module/org_alfresco_module_rm/dod5015/dod5015-model.properties new file mode 100644 index 0000000000..235924f786 --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/dod5015/dod5015-model.properties @@ -0,0 +1,82 @@ +dod_dod5015.description=DOD5015 Content Model + +dod_dod5015.type.dod_filePlan.title=File Plan +dod_dod5015.type.dod_filePlan.description=File Plan + +dod_dod5015.type.dod_recordSeries.title=Record Series +dod_dod5015.type.dod_recordSeries.description=Record Series + +dod_dod5015.type.dod_recordCategory.title=Record Category +dod_dod5015.type.dod_recordCategory.description=Record Category + +dod_dod5015.aspect.dod_scannedRecord.title=Scanned Record +dod_dod5015.aspect.dod_scannedRecord.description=Scanned Record +dod_dod5015.property.dod_scannedFormat.title=Image Format +dod_dod5015.property.dod_scannedFormat.description=Image Format +dod_dod5015.property.dod_scannedFormatVersion.title=Image Format and Version +dod_dod5015.property.dod_scannedFormatVersion.description=Image Format and Version +dod_dod5015.property.dod_resolutionX.title=Image Resolution X +dod_dod5015.property.dod_resolutionX.description=Image Resolution X +dod_dod5015.property.dod_resolutionY.title=Image Resolution Y +dod_dod5015.property.dod_resolutionY.description=Image Resolution Y +dod_dod5015.property.dod_scannedBitDepth.title=Scanned Bit Depth +dod_dod5015.property.dod_scannedBitDepth.description=Scanned Bit Depth + +dod_dod5015.aspect.dod_pdfRecord.title=PDF Record +dod_dod5015.aspect.dod_pdfRecord.description=PDF Record +dod_dod5015.property.dod_producingApplication.title=Producing Application +dod_dod5015.property.dod_producingApplication.description=Producing Application +dod_dod5015.property.dod_producingApplicationVersion.title=Producing Application Version +dod_dod5015.property.dod_producingApplicationVersion.description=Producing Application Version +dod_dod5015.property.dod_pdfVersion.title=PDF Version +dod_dod5015.property.dod_pdfVersion.description=PDF Version +dod_dod5015.property.dod_creatingApplication.title=Creating Application +dod_dod5015.property.dod_creatingApplication.description=Creating Application +dod_dod5015.property.dod_documentSecuritySettings.title=Document Security Settings +dod_dod5015.property.dod_documentSecuritySettings.description=Document Security Settings + +dod_dod5015.aspect.dod_digitalPhotographRecord.title=Digital Photograph Record +dod_dod5015.aspect.dod_digitalPhotographRecord.description=Digital Photograph Record +dod_dod5015.property.dod_caption.title=Caption +dod_dod5015.property.dod_caption.description=Caption +dod_dod5015.property.dod_photographer.title=Photographer +dod_dod5015.property.dod_photographer.description=Photographer +dod_dod5015.property.dod_copyright.title=Copyright +dod_dod5015.property.dod_copyright.description=Copyright +dod_dod5015.property.dod_bitDepth.title=Bit Depth +dod_dod5015.property.dod_bitDepth.description=Bit Depth +dod_dod5015.property.dod_imageSizeX.title=Image Size X +dod_dod5015.property.dod_imageSizeX.description=Image Size X +dod_dod5015.property.dod_imageSizeY.title=Image Size Y +dod_dod5015.property.dod_imageSizeY.description=Image Size Y +dod_dod5015.property.dod_imageSource.title=Image Source +dod_dod5015.property.dod_imageSource.description=Image Source +dod_dod5015.property.dod_compression.title=Compression +dod_dod5015.property.dod_compression.description=Compression +dod_dod5015.property.dod_iccIcmProfile.title=ICC/ICM Profile +dod_dod5015.property.dod_iccIcmProfile.description=ICC/ICM Profile +dod_dod5015.property.dod_exifInformation.title=EXIF Information +dod_dod5015.property.dod_exifInformation.description=EXIF Information + +dod_dod5015.aspect.dod_webRecord.title=Web Record +dod_dod5015.aspect.dod_webRecord.description=Web Record +dod_dod5015.property.dod_webFileName.title=Web File Name +dod_dod5015.property.dod_webFileName.description=Web File Name +dod_dod5015.property.dod_webPlatform.title=Web Platform +dod_dod5015.property.dod_webPlatform.description=Web Platform +dod_dod5015.property.dod_webSiteName.title=Web Site Name +dod_dod5015.property.dod_webSiteName.description=Web Site Name +dod_dod5015.property.dod_webSiteURL.title=Web Site URL +dod_dod5015.property.dod_webSiteURL.description=Web Site URL +dod_dod5015.property.dod_captureMethod.title=Capture Method +dod_dod5015.property.dod_captureMethod.description=Capture Method +dod_dod5015.property.dod_captureDate.title=Capture Date +dod_dod5015.property.dod_captureDate.description=Capture Date +dod_dod5015.property.dod_contact.title=Contact +dod_dod5015.property.dod_contact.description=Contact +dod_dod5015.property.dod_contentManagementSystem.title=Content Management System +dod_dod5015.property.dod_contentManagementSystem.description=Content Management System + +dod_dod5015.aspect.dod_ghosted.title=Ghosted Record +dod_dod5015.aspect.dod_ghosted.description=Ghosted Record + diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/dod5015/dod5015Model.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/dod5015/dod5015Model.xml new file mode 100644 index 0000000000..c96a58b304 --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/dod5015/dod5015Model.xml @@ -0,0 +1,344 @@ + + + + + + + + DOD 5015 Model + Roy Wetherall + 1.0 + + + + + + + + + + + + + + + + + + + + + Image Formats + + + Binary Image Interchange Format (BIIF) + GIF 89a + Graphic Image Format (GIF) 87a + Joint Photographic Experts Group (JPEG) (all versions) + Portable Network Graphics (PNG) 1.0 + Tagged Image Interchange Format (TIFF) 4.0 + TIFF 5.0 + TIFF 6.0 + + + true + + + + + + + + Record Series + rma:recordCategory + + + + + + + + Scanned Record + rma:recordMetaData + + + Image Format + d:text + + true + false + false + + + + Image Format and Version + d:text + true + + true + false + false + + + + + + + Image Resolution X + d:int + true + + + Image Resolution Y + d:int + true + + + Scanned Bit Depth + d:int + false + + + + rma:filePlanComponent + + + + + PDF Record + rma:recordMetaData + + + Producing Application + d:text + true + + true + false + false + + + + Producing Application Version + d:text + true + + + PDF Version + d:text + true + + true + false + false + + + + Creating Application + d:text + false + + true + false + false + + + + Document Security Settings + d:text + false + + true + false + false + + + + + rma:filePlanComponent + + + + + Digital Photograph Record + rma:recordMetaData + + + Caption + d:text + true + + + Photographer + d:text + false + + true + false + false + + + + Copyright + d:text + false + + true + false + false + + + + Bit Depth + d:text + false + + true + false + false + + + + Image Size X + d:int + false + + + Image Size Y + d:int + false + + + Image Source + d:text + false + + true + false + false + + + + Compression + d:text + false + + true + false + false + + + + ICC/ICM Profile + d:text + false + + true + false + false + + + + EXIF Information + d:text + false + + true + false + false + + + + + rma:filePlanComponent + + + + + Web Record + rma:recordMetaData + + + Web File Name + d:text + true + + true + false + false + + + + Web Platform + d:text + true + + true + false + false + + + + Web Site Name + d:text + true + + true + false + false + + + + Web Site URL + d:text + true + + true + false + false + + + + Capture Method + d:text + true + + true + false + false + + + + Capture Date + d:date + true + + + Contact + d:text + true + + true + false + false + + + + Content Management System + d:text + false + + true + false + false + + + + + rma:filePlanComponent + + + + + + \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/log4j.properties b/rm-server/config/alfresco/module/org_alfresco_module_rm/log4j.properties new file mode 100644 index 0000000000..bdf0de5022 --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/log4j.properties @@ -0,0 +1 @@ +log4j.logger.org.alfresco.module.org_alfresco_module_rm.caveat=warn diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/action-service.properties b/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/action-service.properties new file mode 100644 index 0000000000..c55856199e --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/action-service.properties @@ -0,0 +1,37 @@ +rm.action.not-defined=The records management action {0} has not been defined. +rm.action.no-implicit-noderef=Unable to execute the records management action, because the action {0} implementation does not provide an implicit nodeRef. +rm.action.record-not-declared=Unable to execute disposition action {0}, because the record is not declared. (actionedUponNodeRef={1}) +rm.action.expected-record-level=Unable to execute disposition action {0}, because disposition is expected at the record level and this node is not a record. (actionedUponNodeRef={1}) +rm.action.not-all-records-declared=Unable to execute disposition action {0}, because not all the records in the record are declared. (actionedUponNodeRef={1}) +rm.action.not-eligible=Unable to execute disposition action {0}, because the next disposition action on the record or record folder is not eligible. (actionedUponNodeRef={1}) +rm.action.no-disposition-instructions=Unable to find disposition instructions for node. Can not execute disposition action {0}. (nodeRef={1}) +rm.action.no-disposition-lisfecycle-set=Unable to execute disposition action {0}, because node does not have a disposition life-cycle set. (nodeRef={1}) +rm.action.next-disp-not-set=Unable to execute disposition action {0}, because the next disposition action is not set. (nodeRef={1}) +rm.action.not-next-disp=Unable to execute disposition action {0}, because this is not the next disposition action for this record or record folder. (nodeRef={1}) +rm.action.not-record-folder=Unable to execute disposition action {0}, because disposition is expected at the record folder level and this node is not a record folder. (nodeRef={1}) +rm.action.actioned-upon-not-record=Can not execute action {0}, because the actioned upon node is not a Record. (filePlanComponet={1}) +rm.action.custom-aspect-not-recognised=The custom type can not be applied, because is it not recognised. (customAspect={0}) +rm.action.close-record-folder-not-folder=Unable to close record folder, because the node is not a record folder. (nodeRef={0}) +rm.action.event-no-disp-lc=The event {0} can not be completed, because it is not defined on the disposition lifecycle. +rm.action.undeclared-only-records=Only records can be undeclared. (nodeRef={0}) +rm.action.no-declare-mand-prop=Can not declare record, because not all the records mandatory properties have been set. +rm.action.ghosted-prop-update=The content properties of a previously destroyed record can not be updated. +rm.action.valid-date-disp-asof=A valid date must be specified when setting the disposition action as of date. +rm.action.disp-asof-lifecycle-applied=It is invalid to edit the disposition as of date of a record or record folder which has a lifecycle applied. +rm.action.hold-edit-reason-none=Can not edit hold reason, because no reason has been given. +rm.action.hold-edit-type=Can not edit hold reason, because actioned upon node is not of type {0}. (nodeRef={1}) +rm.action.specify-avlid-date=Must specify a valid date when setting the review as of date. +rm.action.review-details-only=Can only edit the review details of vital records. +rm.action.freeze-no-reason=Can not freeze a record without a reason. +rm.action.freeze-only-records-folders=Can only freeze records or record folders. +rm.action.no-open-record-folder=Unable to open record folder, because node is not a record folder. (actionedUponNodeRef={0}) +rm.action.not-hold-type=Can not relinquish hold, because node is not of type {0}. (actionedUponNodeRef={1}) +rm.action.no-read-mime-message=Unable to read mime message, because {0}. +rm.action.email-declared=Can not split email, because record has already been declared. (actionedUponNodeRef={0}) +rm.action.email-not-record=Can no split email, because node is not a record. (actionedUponNodeRef={0}) +rm.action.email-create-child-assoc=Unable to create custom child association. +rm.action.node-already-transfer=Node is already being transfered. +rm.action.node-not-transfer=Node is not a transfer object. +rm.action.undo-not-last=Can not undo cut off, because last disposition action was not cut off. +rm.action.records_only_undeclared=Only records can be undeclared. +rm.action.event-not-undone=The event {0} can not be undone, because it is not defined on the disposition lifecycle. \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/admin-service.properties b/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/admin-service.properties new file mode 100644 index 0000000000..09d209b7dd --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/admin-service.properties @@ -0,0 +1,21 @@ +rm.admin.service-not-init=The customisation service has not been initialised. +rm.admin.not-customisable=The class {0} is not customisable. +rm.admin.invalid-custom-aspect=Unable to find custom aspect {0} for customisable class {1}. +rm.admin.property-already-exists=Property {0} already exists. +rm.admin.cannot-apply-constraint=Cannot apply constraint {0} to property {1} with datatype {2}. (expected: dataType = TEXT) +rm.admin.prop-exist=Can not find custom property {0}. +rm.admin.custom-prop-exist=Custom model does not contain property {0}. +rm.admin.unknown-aspect=Unknown aspect {0}. +rm.admin.ref-exist= Can not find custom reference {0}. +rm.admin.ref-label-in-use=Reference label {0} is already in use. +rm.admin.assoc-exists=Association {0} already exists. +rm.admin.child-assoc-exists=Child association {0} already exists. +rm.admin.cannot-find-assoc-def=Can not find association definition {0}. +rm.admin.constraint-exists=Constraint {0} already exists. +rm.admin.contraint-cannot-find=Can not find definition for constraint {0}. +rm.admin.unexpected_type_constraint=Unexpected type {0} for constraint {1}, expected {2}. +rm.admin.custom-model-not-found=Custom model {0} can not be found. +rm.admin.custom-model-no-content=Custom model has no content. (nodeRef={0}) +rm.admin.error-write-custom-model=Error writing custom model content. (nodeRef={0}). +rm.admin.error-client-id=Error generating QName, because client id is already in use. (clientid={0}) +rm.admin.error-split-id=Unable to split id {0}, because separator {1} is not present. \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/audit-service.properties b/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/audit-service.properties new file mode 100644 index 0000000000..6f6d53ded6 --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/audit-service.properties @@ -0,0 +1,34 @@ +rm.audit.updated-metadata=Updated Metadata +rm.audit.created-object=Created Object +rm.audit.delte-object=Delete Object +rm.audit.login-succeeded=Login Succeeded +rm.audit.login-failed=Login Failed +rm.audit.filed-record=Filed Record +rm.audit.reviewed=Reviewed +rm.audit.cut-off=Cut Off +rm.audit.reversed-cut-off=Reversed Cut Off +rm.audit.destroyed-item=Destroyed Item +rm.audit.opened-record-folder=Opened Record Folder +rm.audit.closed-record-folder=Closed Record Folder +rm.audit.setup-recorder-folder=Setup Recorder Folder +rm.audit.declared-record=Declared Record +rm.audit.undeclared-record=Undeclared Record +rm.audit.froze-item=Froze Item +rm.audit.relinquised-hold=Relinquished Hold +rm.audit.updated-hold-reason=Updated Hold Reason +rm.audit.updated-review-as-of-date=Updated Review As Of Date +rm.audit.updated-disposition-as-of-date=Updated Disposition As Of Date +rm.audit.updated-vital-record-definition=Updated Vital Record Definition +rm.audit.updated-disposition-action-definition=Updated Disposition Action Definition +rm.audit.completed-event=Completed Event +rm.audit.revered-complete-event=Reversed Completed Event +rm.audit.transferred-item=Transferred Item +rm.audit.completed-transfer=Completed Transfer +rm.audit.accession=Accession +rm.audit.copmleted-accession=Completed Accession +rm.audit.scanned-record=Set Record As A Scanned Record +rm.audit.pdf-record=Set Record As PDF A Record +rm.audit.photo-record=Set Record As A Digital Photographic Record +rm.audit.web-record=Set Record As A Web Record +rm.audit.trail-file-fail=Failed to generate audit trail file. +rm.audit.audit-report=Audit Report \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/notification-service.properties b/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/notification-service.properties new file mode 100644 index 0000000000..60d1ffdc22 --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/notification-service.properties @@ -0,0 +1,2 @@ +notification.dueforreview.subject=Records Due For Review Notification +notification.superseded.subject=Record Superseded Notification \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/records-management-service.properties b/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/records-management-service.properties new file mode 100644 index 0000000000..33b3721073 --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/records-management-service.properties @@ -0,0 +1,17 @@ +rm.service.error-add-content-container=Content can not be added to a record container. Please use record folders to file content. +rm.service.update-disposition-action-def=Can not update the disposition action definition, because an update is being published. +rm.service.set-id=The identifier property value of the object {0} can not be set, because it is read only. +rm.service.path-node=Unable to get path for node. (nodeRef={0}) +rm.service.invalid-rm-node=Invalid records management node, because aspect {0} is not present. +rm.service.no-root=Unable to find records management root. +rm.service.dup-root=Can not create the records management root, because there is already a records management root in this hierarchy. +rm.service.root-type=Can not create the records management root, because type {0} is not a sub-type of rm:recordsManagementRootContainer. +rm.service.container-parent-type=Can not create records management container, because parent was not sub-type of rm:recordsManagement (parentType={0}) +rm.service.container-type=Can not create records management container, because type {0} is not a sub-type of rm:recordsManagementContainer. +rm.service.container-expected=Node reference to a rm:recordsManagementContainer node expected. +rm.service.record-folder-expected=Node reference to a rm:recordFolder node expected. +rm.service.parent-record-folder-root=Can not create a record folder, because the parent is a records management root. +rm.service.parent-record-folder-type=Can not create record folder, because parent was not sub-type of rm:recordsManagementContainer. (parentType={0}) +rm.service.record-folder-type=Can not create record folder, because provided type is not a sub-type of rm:recordFolder. (type={0}) +rm.service.not-record=The node {0} is not a record. +rm.service.vital-def-missing=Vital record definition aspect is not present on node. (nodeRef={0}) \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/records-model.properties b/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/records-model.properties new file mode 100644 index 0000000000..46622281e4 --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/records-model.properties @@ -0,0 +1,273 @@ +rma_recordsmanagement.description=Records Management Content Model + +rma_recordsmanagement.type.rma_rmsite.title=Records Management Site +rma_recordsmanagement.type.rma_rmsite.description=Specialised Site for Records Management + +rma_recordsmanagement.type.rma_caveatConfig.title=Caveat Config +rma_recordsmanagement.type.rma_caveatConfig.decription=Caveat Config + +rma_recordsmanagement.type.rma_emailConfig.title=EMail Configuration +rma_recordsmanagement.type.rma_emailConfig.decription=Email Configuration + +rma_recordsmanagement.type.rma_recordsManagementContainer.title=Records Management Container +rma_recordsmanagement.type.rma_recordsManagementContainer.decription=Records Management Container + +rma_recordsmanagement.type.rma_recordsManagementRootContainer.title=Records Management Root Container +rma_recordsmanagement.type.rma_recordsManagementRootContainer.decription=Records Management Root Container + +rma_recordsmanagement.type.rma_dispositionSchedule.title=Disposition Schedule +rma_recordsmanagement.type.rma_dispositionSchedule.decription=Disposition Schedule + +rma_recordsmanagement.property.rma_dispositionAuthority.title=Disposition Authority +rma_recordsmanagement.property.rma_dispositionAuthority.decription=Disposition Authority + +rma_recordsmanagement.property.rma_dispositionInstructions.title=Disposition Instructions +rma_recordsmanagement.property.rma_dispositionInstructions.decription=Disposition Instructions + +rma_recordsmanagement.property.rma_recordLevelDisposition.title=Record Level Disposition +rma_recordsmanagement.property.rma_recordLevelDisposition.decription=Record Level Disposition + +rma_recordsmanagement.association.rma_dispositionActionDefinitions.title=Disposition Actions +rma_recordsmanagement.association.rma_dispositionActionDefinitions.decription=Disposition Actions + +rma_recordsmanagement.type.rma_dispositionActionDefinition.title=Disposition Action Definition +rma_recordsmanagement.type.rma_dispositionActionDefinition.decription=Disposition Action Definition +rma_recordsmanagement.property.rma_dispositionActionName.title=Disposition Action Name +rma_recordsmanagement.property.rma_dispositionActionName.decription=Disposition Action Name +rma_recordsmanagement.property.rma_dispositionDescription.title=Disposition Description +rma_recordsmanagement.property.rma_dispositionDescription.decription=Disposition Description +rma_recordsmanagement.property.rma_dispositionLocation.title=Disposition Location +rma_recordsmanagement.property.rma_dispositionLocation.decription=Disposition Location +rma_recordsmanagement.property.rma_dispositionPeriod.title=Disposition Period +rma_recordsmanagement.property.rma_dispositionPeriod.decription=Disposition Period +rma_recordsmanagement.property.rma_dispositionPeriodProperty.title=Disposition Period Property +rma_recordsmanagement.property.rma_dispositionPeriodProperty.decription=Disposition Period Property +rma_recordsmanagement.property.rma_dispositionEvent.title=Disposition Event +rma_recordsmanagement.property.rma_dispositionEvent.decription=Disposition Event +rma_recordsmanagement.property.rma_dispositionEventCombination.title=Disposition Event Combination +rma_recordsmanagement.property.rma_dispositionEventCombination.decription=Disposition Event Combination + +rma_recordsmanagement.type.rma_recordFolder.title=Record Folder +rma_recordsmanagement.type.rma_recordFolder.decription=Record Folder +rma_recordsmanagement.property.rma_isClosed.title=Record +rma_recordsmanagement.property.rma_isClosed.decription=Record + +rma_recordsmanagement.type.rma_nonElectronicDocument.title=Non-Electronic Document +rma_recordsmanagement.type.rma_nonElectronicDocument.decription=Non-Electronic Document +rma_recordsmanagement.property.rma_physicalSize.title=Physical Size +rma_recordsmanagement.property.rma_physicalSize.decription=The size of the document measured in linear meters +rma_recordsmanagement.property.rma_numberOfCopies.title=Number Of Copies +rma_recordsmanagement.property.rma_numberOfCopies.description=The number of copies of the document +rma_recordsmanagement.property.rma_storageLocation.title=Storage Location +rma_recordsmanagement.property.rma_storageLocation.decription=The physical storage location of the record. +rma_recordsmanagement.property.rma_shelf.title=Shelf +rma_recordsmanagement.property.rma_shelf.decription=The shelf the record resides on. +rma_recordsmanagement.property.rma_box.title=Box +rma_recordsmanagement.property.rma_box.description=The box the record resides in. +rma_recordsmanagement.property.rma_file.title=File +rma_recordsmanagement.property.rma_file.decription=The file the record resides in. + +rma_recordsmanagement.type.rma_dispositionAction.title=Disposition Action +rma_recordsmanagement.type.rma_dispositionAction.decription=Disposition Action +rma_recordsmanagement.property.rma_dispositionActionId.title=Disposition Action Id +rma_recordsmanagement.property.rma_dispositionActionId.decription=Disposition Action Id +rma_recordsmanagement.property.rma_dispositionAction.title=Disposition Action +rma_recordsmanagement.property.rma_dispositionAction.decription=Disposition Action +rma_recordsmanagement.property.rma_dispositionAsOf.title=Disposition Action +rma_recordsmanagement.property.rma_dispositionAsOf.decription=Disposition Action +rma_recordsmanagement.property.rma_dispositionEventsEligible.title=Disposition Events Eligible +rma_recordsmanagement.property.rma_dispositionEventsEligible.decription=Disposition Events Eligible +rma_recordsmanagement.property.rma_dispositionActionStartedAt.title=Disposition Action Started At +rma_recordsmanagement.property.rma_dispositionActionStartedAt.decription=Disposition Action Started At +rma_recordsmanagement.property.rma_dispositionActionStartedBy.title=Disposition Action Started By +rma_recordsmanagement.property.rma_dispositionActionStartedBy.decription=Disposition Action Started By +rma_recordsmanagement.property.rma_dispositionActionCompletedAt.title=Disposition Action Completed At +rma_recordsmanagement.property.rma_dispositionActionCompletedAt.decription=Disposition Action Completed At +rma_recordsmanagement.property.rma_dispositionActionCompletedBy.title=Disposition Action Copmleted By +rma_recordsmanagement.property.rma_dispositionActionCompletedBy.decription=Disposition Action Copmleted By +rma_recordsmanagement.association.rma_eventExecutions.title=Event executions +rma_recordsmanagement.association.rma_eventExecutions.decription=Event executions + +rma_recordsmanagement.type.rma_eventExecution.title=Event Execution +rma_recordsmanagement.type.rma_eventExecution.decription=Event Execution +rma_recordsmanagement.property.rma_eventExecutionName.title=Event Name +rma_recordsmanagement.property.rma_eventExecutionName.decription=Event Name +rma_recordsmanagement.property.rma_eventExecutionAutomatic.title=Event automatic +rma_recordsmanagement.property.rma_eventExecutionAutomatic.decription=Event automatic +rma_recordsmanagement.property.rma_eventExecutionComplete.title=Event complete +rma_recordsmanagement.property.rma_eventExecutionComplete.decription=Event complete +rma_recordsmanagement.property.rma_eventExecutionCompletedBy.title=Event completed by +rma_recordsmanagement.property.rma_eventExecutionCompletedBy.decription=Event completed by +rma_recordsmanagement.property.rma_eventExecutionCompletedAt.title=Event completed at +rma_recordsmanagement.property.rma_eventExecutionCompletedAt.decription=Event completed at + +rma_recordsmanagement.type.rma_hold.title=Hold +rma_recordsmanagement.type.rma_hold.decription=Hold +rma_recordsmanagement.property.rma_holdReason.title=Hold Reason +rma_recordsmanagement.property.rma_holdReason.decription=Hold Reason +rma_recordsmanagement.association.rma_frozenRecords.title=Frozen Records +rma_recordsmanagement.association.rma_frozenRecords.decription=Frozen Records + +rma_recordsmanagement.type.rma_transfer.title=Transfer +rma_recordsmanagement.type.rma_transfer.decription=Transfer +rma_recordsmanagement.property.rma_transferAccessionIndicator.title=Transfer Accession Indicator +rma_recordsmanagement.property.rma_transferAccessionIndicator.decription=Transfer Accession Indicator +rma_recordsmanagement.property.rma_transferPDFIndicator.title=Transfer PDF Indicator +rma_recordsmanagement.property.rma_transferPDFIndicator.decription=Transfer PDF Indicator +rma_recordsmanagement.property.rma_transferLocation.title=Transfer PDF +rma_recordsmanagement.property.rma_transferLocation.decription=Transfer PDF +rma_recordsmanagement.association.rma_transferred.title=Transferred +rma_recordsmanagement.association.rma_transferred.decription=Transferred + +rma_recordsmanagement.aspect.rma_filePlanComponent.title=File Plan Component +rma_recordsmanagement.aspect.rma_filePlanComponent.decription=File Plan Component +rma_recordsmanagement.property.rma_rootNodeRef.title=Root node +rma_recordsmanagement.property.rma_rootNodeRef.decription=Root node + +rma_recordsmanagement.aspect.rma_recordsManagementRoot.title=Records Management Root +rma_recordsmanagement.aspect.rma_recordsManagementRoot.decription=Records Management Root +rma_recordsmanagement.association.rma_holds.title=Holds +rma_recordsmanagement.association.rma_holds.decription=Holds +rma_recordsmanagement.association.rma_transfers.title=Transfers +rma_recordsmanagement.association.rma_transfers.decription=Transfers + +rma_recordsmanagement.aspect.rma_declaredRecord.title=Declared Record +rma_recordsmanagement.aspect.rma_declaredRecord.decription=Declared Record +rma_recordsmanagement.property.rma_declaredAt.title=Date Declared +rma_recordsmanagement.property.rma_declaredAt.decription=Date Declared +rma_recordsmanagement.property.rma_declaredBy.title=Declared By +rma_recordsmanagement.property.rma_declaredBy.decription=Declared By + +rma_recordsmanagement.aspect.rma_recordComponentIdentifier.title=Record component identifier +rma_recordsmanagement.aspect.rma_recordComponentIdentifier.decription=Record component identifier +rma_recordsmanagement.property.rma_identifier.title=Identifier +rma_recordsmanagement.property.rma_identifier.decription=Unique record identifier +rma_recordsmanagement.property.rma_dbUniquenessId.title=Database uniqueness +rma_recordsmanagement.property.rma_dbUniquenessId.decription=Database uniqueness + +rma_recordsmanagement.aspect.rma_vitalRecordDefinition.title=Vital Record Definition +rma_recordsmanagement.aspect.rma_vitalRecordDefinition.decription=Vital Record Definition + +rma_recordsmanagement.property.rma_reviewPeriod.title=Review Period +rma_recordsmanagement.property.rma_reviewPeriod.decription=Review Period +rma_recordsmanagement.property.rma_vitalRecordIndicator.title=Vital Record Indicator +rma_recordsmanagement.property.rma_vitalRecordIndicator.decription=Vital Record Indicator + +rma_recordsmanagement.aspect.rma_record.title=Record +rma_recordsmanagement.aspect.rma_record.decription=Record +rma_recordsmanagement.property.rma_dateFiled.title=Date Filed +rma_recordsmanagement.property.rma_dateFiled.decription=Date Filed +rma_recordsmanagement.property.rma_publicationDate.title=Publication Date +rma_recordsmanagement.property.rma_publicationDate.decription=Publication Date +rma_recordsmanagement.property.rma_originator.title=Originator +rma_recordsmanagement.property.rma_originator.decription=Originator +rma_recordsmanagement.property.rma_originatingOrganization.title=Originating Organization +rma_recordsmanagement.property.rma_originatingOrganization.decription=Originating Organization +rma_recordsmanagement.property.rma_mediaType.title=Media Type +rma_recordsmanagement.property.rma_mediaType.decription=Media Type +rma_recordsmanagement.property.rma_format.title=Format +rma_recordsmanagement.property.rma_format.decription=Format +rma_recordsmanagement.property.rma_dateReceived.title=Date Received +rma_recordsmanagement.property.rma_dateReceived.decription=Date Received +rma_recordsmanagement.property.rma_address.title=Addressee +rma_recordsmanagement.property.rma_address.decription=Addressee +rma_recordsmanagement.property.rma_otherAddress.title=Other Addressee +rma_recordsmanagement.property.rma_otherAddress.decription=Other Addressee + +rma_recordsmanagement.aspect.rma_recordMetaData.title=Record Meta-data +rma_recordsmanagement.aspect.rma_recordMetaData.description=Marker aspect for record meta-data + +rma_recordsmanagement.aspect.rma_commonRecordDetails.title=Common Records Details +rma_recordsmanagement.aspect.rma_commonRecordDetails.description=Meta-data common to all record types +rma_recordsmanagement.property.rma_location.title=Location +rma_recordsmanagement.property.rma_location.decription=Location + +rma_recordsmanagement.aspect.rma_vitalRecord.title=Vital Record +rma_recordsmanagement.aspect.rma_vitalRecord.decription=Vital Record +rma_recordsmanagement.property.rma_reviewAsOf.title=Next Review +rma_recordsmanagement.property.rma_reviewAsOf.decription=Next Review +rma_recordsmanagement.property.rma_notificationIssued.title=Indicates n that this record is due for review has been issued +rma_recordsmanagement.property.rma_notificationIssued.decription=Indicates n that this record is due for review has been issued + +rma_recordsmanagement.aspect.rma_scheduled.title=Scheduled +rma_recordsmanagement.aspect.rma_scheduled.decription=Scheduled +rma_recordsmanagement.association.rma_dispositionSchedule.title=Disposition Schedule +rma_recordsmanagement.association.rma_dispositionSchedule.decription=Disposition Schedule + +rma_recordsmanagement.aspect.rma_dispositionLifecycle.title=Disposition Lifecycle +rma_recordsmanagement.aspect.rma_dispositionLifecycle.decription=Disposition Lifecycle +rma_recordsmanagement.association.rma_nextDispositionAction.title=Next disposition action +rma_recordsmanagement.association.rma_nextDispositionAction.decription=Next disposition action +rma_recordsmanagement.association.rma_dispositionActionHistory.title=Disposition Action History +rma_recordsmanagement.association.rma_dispositionActionHistory.decription=Disposition Action History + +rma_recordsmanagement.aspect.rma_cutOff.title=Cut Off +rma_recordsmanagement.aspect.rma_cutOff.decription=Cut Off +rma_recordsmanagement.property.rma_cutOffDate.title=Cut Off Date +rma_recordsmanagement.property.rma_cutOffDate.decription=Cut Off Date + +rma_recordsmanagement.aspect.rma_transferred.title=Transferred +rma_recordsmanagement.aspect.rma_transferred.decription=Transferred + +rma_recordsmanagement.aspect.rma_ascended.title=Ascended +rma_recordsmanagement.aspect.rma_ascended.decription=Ascended + +rma_recordsmanagement.aspect.rma_frozen.title=Frozen +rma_recordsmanagement.aspect.rma_frozen.decription=Frozen +rma_recordsmanagement.property.rma_frozenAt.title=Frozen At +rma_recordsmanagement.property.rma_frozenAt.decription=Frozen At +rma_recordsmanagement.property.rma_frozenBy.title=Frozen By +rma_recordsmanagement.property.rma_frozenBy.decription=Frozen By + +rma_recordsmanagement.aspect.rma_caveatConfigRoot.title=Caveat Configuration Root +rma_recordsmanagement.aspect.rma_caveatConfigRoot.decription=Caveat Configuration Root +rma_recordsmanagement.association.rma_caveatConfigAssoc.title=Caveat Configuration +rma_recordsmanagement.association.rma_caveatConfigAssoc.description=Caveat Configuration + +rma_recordsmanagement.aspect.rma_emailConfigRoot.title=EmMil Config Root +rma_recordsmanagement.aspect.rma_emailConfigRoot.decription=EMail Config Root +rma_recordsmanagement.association.rma_emailConfigAssoc.title=EMail Configuration +rma_recordsmanagement.association.rma_emailConfigAssoc.description=EMail Configuration + +rma_recordsmanagement.aspect.rma_recordSearch.title=Record Search +rma_recordsmanagement.aspect.rma_recordSearch.decription=Rolled up search information to support Records Management search +rma_recordsmanagement.property.rma_recordSearchHasDispositionSchedule.title=Has Disposition Schedule +rma_recordsmanagement.property.rma_recordSearchHasDispositionSchedule.description=Indicates whether the item has an associated disposition schedule +rma_recordsmanagement.property.rma_recordSearchDispositionActionName.title=Disposition Action Name +rma_recordsmanagement.property.rma_recordSearchDispositionActionName.description=The name of the next disposition action +rma_recordsmanagement.property.rma_recordSearchDispositionActionAsOf.title=Disposition Action Of +rma_recordsmanagement.property.rma_recordSearchDispositionActionAsOf.description=The date at which the next disposation action becomes eligible +rma_recordsmanagement.property.rma_recordSearchDispositionPeriod.title=Disposition Period +rma_recordsmanagement.property.rma_recordSearchDispositionPeriod.description=Disposition Period +rma_recordsmanagement.property.rma_recordSearchDispositionPeriodExpression.title=Disposition Period Expression +rma_recordsmanagement.property.rma_recordSearchDispositionPeriodExpression.description=Disposition Period Expression +rma_recordsmanagement.property.rma_recordSearchDispositionEventsEligible.title=Disposition Events Eligible +rma_recordsmanagement.property.rma_recordSearchDispositionEventsEligible.description=Disposition Events Eligible +rma_recordsmanagement.property.rma_recordSearchDispositionEvents.title=Disposition Events +rma_recordsmanagement.property.rma_recordSearchDispositionEvents.description=Disposition Events +rma_recordsmanagement.property.rma_recordSearchDispositionAuthority.title=Disposition Authority +rma_recordsmanagement.property.rma_recordSearchDispositionAuthority.description=Disposition Authority +rma_recordsmanagement.property.rma_recordSearchDispositionInstructions.title=Disposition Instructions +rma_recordsmanagement.property.rma_recordSearchDispositionInstructions.description=Disposition Instructions +rma_recordsmanagement.property.rma_recordSearchHoldReason.title=Hold Reason +rma_recordsmanagement.property.rma_recordSearchHoldReason.description=Hold Reason +rma_recordsmanagement.property.rma_recordSearchVitalRecordReviewPeriod.title=Vital Record Review Period +rma_recordsmanagement.property.rma_recordSearchVitalRecordReviewPeriod.description=Vital Record Review Period +rma_recordsmanagement.property.rma_recordSearchVitalRecordReviewPeriodExpression.title=Review Period Expression +rma_recordsmanagement.property.rma_recordSearchVitalRecordReviewPeriodExpression.description=Review Period Expression + +rma_recordsmanagement.aspect.rma_versionedRecord.title=Versioned Record +rma_recordsmanagement.aspect.rma_versionedRecord.decription=Versioned Record + +rma_recordsmanagement.aspect.rma_unpublishedUpdate.title=Unpublished Update +rma_recordsmanagement.aspect.rma_unpublishedUpdate.decription=Unpublished Update +rma_recordsmanagement.property.rma_unpublishedUpdate.title=Unpublished Update +rma_recordsmanagement.property.rma_unpublishedUpdate.description=Indicates whether there is an unpublished update +rma_recordsmanagement.property.rma_updateTo.title=Update To +rma_recordsmanagement.property.rma_updateTo.description=Destination of the update +rma_recordsmanagement.property.rma_updatedProperties.title=Updated Properties +rma_recordsmanagement.property.rma_updatedProperties.description=The updated properties +rma_recordsmanagement.property.rma_publishInProgress.title=Publish In Progress +rma_recordsmanagement.property.rma_publishInProgress.description=Indicates whether a publish is currently in progress + + \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/model/recordsModel.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/model/recordsModel.xml new file mode 100644 index 0000000000..139e1dbe3d --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/model/recordsModel.xml @@ -0,0 +1,1117 @@ + + + + + + + + + Records Management Model + Roy Wetherall + 1.0 + + + + + + + + + + + + + + + + + + + + + + + RM Site + st:site + + + + + Caveat Config + cm:content + false + + rma:filePlanComponent + + + + + Email Config + cm:content + + rma:filePlanComponent + + + + + + + + Records Management Container + cm:folder + false + + + cm:titled + rma:recordComponentIdentifier + rma:filePlanComponent + + + + + + + Record Category + rma:recordsManagementContainer + + + rma:vitalRecordDefinition + + + + + + + File Plan + rma:recordsManagementContainer + + rma:recordsManagementRoot + + + + + + + + Disposition Schedule + cm:cmobject + + + + + Disposition Authority + d:text + true + + true + false + false + + + + + Disposition Instructions + d:text + true + + + + Record Level Disposition + d:boolean + true + false + + + + + + + + Disposition Actions + + false + false + + + rma:dispositionActionDefinition + false + true + + + + + + + rma:filePlanComponent + + + + + + Disposition Action Definition + cm:cmobject + + + + + Disposition Action Name + d:text + true + + + + Disposition Description + d:text + false + + + + Disposition Location + d:text + false + + true + false + false + + + + + Disposition Period + d:period + false + none|0 + + + + Disposition Period Property + d:text + false + + true + false + false + + + + + Disposition Event + d:text + true + + true + false + false + + + + + Disposition Event Combination + d:text + true + or + + true + false + false + + + + + + rma:filePlanComponent + + + + + + Record Folder + cm:folder + false + + + + + + + + Record Folder Closed + Indicates whether the folder is closed + d:boolean + true + true + false + + + + + + cm:titled + rma:recordComponentIdentifier + rma:commonRecordDetails + rma:filePlanComponent + + + + + + + Non-Electronic Document + cm:content + false + + + + Document Physical Size + d:int + false + + true + false + false + + + + + Number Of Copies + d:int + false + 1 + + true + false + false + + + + + Storage Location + d:text + false + + true + false + false + + + + + Shelf + d:text + false + + true + false + false + + + + + Box + d:text + false + + true + false + false + + + + + File + d:text + false + + true + false + false + + + + + + rma:filePlanComponent + + + + + Disposition Action + cm:cmobject + + + Disposition Action Id + d:text + true + + true + false + false + + + + Disposition Action + d:text + true + + true + false + false + + + + Disposition Action Date + d:date + false + + + Disposition Events Eligible + d:boolean + false + + + Disposition Action Started At + d:date + false + + + Disposition Action Started By + d:text + false + + true + false + false + + + + Disposition Action Completed At + d:date + false + + + Disposition Action Copmleted By + d:text + false + + true + false + false + + + + + + + + + Event executions + + false + false + + + rma:eventExecution + false + true + + + + + + + rma:filePlanComponent + + + + + + + Event Execution + Execution details of an event + cm:cmobject + + + + + Event Name + d:text + true + + true + false + false + + + + Event automatic + d:boolean + true + + + Event complete + d:boolean + true + false + + + Event completed by + d:text + false + + true + false + false + + + + Event completed at + d:date + false + + + + + + rma:filePlanComponent + + + + + + Hold + cm:folder + false + + + + + Hold Reason + d:text + true + + + + + + + + Frozen Records + + false + true + + + rma:filePlanComponent + false + true + + + + + + + cm:titled + rma:filePlanComponent + + + + + + Transfer + cm:folder + false + + + + + Transfer Accession Indicator + d:boolean + true + true + + + + Transfer PDF Indicator + Indicates that transfer includes PDF + d:boolean + true + false + + + + Transfer PDF Indicator + Transfer Location + d:text + + + + + + + + Transferred + + false + false + + + rma:dispositionLifecycle + false + true + + + + + + + cm:titled + rma:filePlanComponent + + + + + + + + + + + + + File Plan Component + false + + + Root node reference + d:noderef + + + + + + Records Management Root + + + + Holds + + false + false + + + rma:hold + false + true + + + + + + Transfers + + false + false + + + rma:transfer + false + true + + + + + + rma:filePlanComponent + + + + + Declared Record + + + Date Declared + d:date + + + Declared By + d:text + + true + false + false + + + + + rma:filePlanComponent + + + + + Record component identifier + + + Record Component Identifier + d:text + true + + true + false + false + + + + Database uniqueness id + d:text + true + false + + + + rma:filePlanComponent + + + + + Vital Record Definition + + + Review Period + d:period + true + none|0 + + + Vital Record Indicator + d:boolean + true + false + + + + rma:filePlanComponent + + + + + + + Record + + false + + + + + + + + Date Filed + d:date + true + + + + Publication Date + d:date + true + + + + Originator + d:text + true + + true + false + false + + + + + Originating Organization + d:text + true + + true + false + false + + + + + Media Type + d:text + false + + true + false + false + + + + + Format + d:text + false + + true + false + false + + + + + Date Received + d:date + false + + + + + Addressee + d:text + false + + true + false + false + + + + Other Addressee + d:text + false + + true + false + false + + + + + + + cm:titled + rma:recordComponentIdentifier + rma:commonRecordDetails + rma:filePlanComponent + + + + + + + + + + + + Location + d:text + false + + true + false + false + + + + + + rma:filePlanComponent + + + + + + + Vital Record + + + Next Review Date + d:date + false + + + Indicates whether a notification that this record is due for review has been issued + d:boolean + false + false + + + + rma:filePlanComponent + + + + + Scheduled + + + + Disposition Schedule + + false + false + + + rma:dispositionSchedule + false + false + + + + + + rma:filePlanComponent + + + + + Disposition Lifecycle + + + + Next disposition action + + false + false + + + rma:dispositionAction + false + false + + + + + + Disposition Action History + + false + false + + + rma:dispositionAction + false + true + + + + + + rma:filePlanComponent + + + + + + Cut Off + + + Cut Off Date + d:date + true + + + + + + + TransferringS + + + + + Transferred + + + + + Ascended + + + + Frozen + + + Frozen At Date + d:date + true + + + Frozen By + d:text + true + + true + false + false + + + + + rma:filePlanComponent + + + + + Caveat Config Root + + + + true + false + + + rma:caveatConfig + false + false + + false + + + + + + + Email Config Root + + + + true + false + + + rma:emailConfig + false + false + + false + + + + + + + + Record Search + + + d:boolean + + + d:text + + true + false + false + + + + d:date + + + d:text + + true + false + false + + + + d:text + + true + false + false + + + + d:boolean + + + d:text + true + + true + false + false + + + + d:text + + true + false + false + + + + d:text + + + d:text + + + d:text + + true + false + false + + + + d:text + + true + false + false + + + + + + + Versioned Record + + + + Unpublished Update + + + d:boolean + true + true + + + d:text + + + d:any + + + d:boolean + true + false + + + + + + + Ghosted Record + false + + + + + \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/model/recordsPermissionModel.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/model/recordsPermissionModel.xml new file mode 100644 index 0000000000..e6f1383dce --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/model/recordsPermissionModel.xml @@ -0,0 +1,490 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml new file mode 100644 index 0000000000..e475dac07e --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + /${spaces.company_home.childname}/${spaces.dictionary.childname} + alfresco/module/org_alfresco_module_rm/bootstrap/RMDataDictionaryBootstrap.xml + + + + + + + + + + alfresco.module.org_alfresco_module_rm.messages.notification-service + alfresco.module.org_alfresco_module_rm.messages.admin-service + alfresco.module.org_alfresco_module_rm.messages.records-management-service + alfresco.module.org_alfresco_module_rm.messages.action-service + alfresco.module.org_alfresco_module_rm.messages.audit-service + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + rmService + + + + + + + + + + + alfresco.module.org_alfresco_module_rm.rm-events + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + caveatConfig + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + EEE, d MMM yyyy HH:mm:ss Z + EEE, d MMM yy HH:mm:ss Z + + + + + + + + + + + + + diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/module.properties b/rm-server/config/alfresco/module/org_alfresco_module_rm/module.properties new file mode 100644 index 0000000000..c5dde346e8 --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/module.properties @@ -0,0 +1,11 @@ +# Alfresco Records Management Module +module.id=org_alfresco_module_rm + +# 23/02/2012 - Renamed +module.aliases=org_alfresco_module_dod5015 + +module.title=Records Management +module.description=Alfresco Record Management Extension +module.version=2.0 + +module.repo.version.min=4.0 \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-action-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-action-context.xml new file mode 100644 index 0000000000..9fe28aa465 --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-action-context.xml @@ -0,0 +1,811 @@ + + + + + + + + + + alfresco.module.org_alfresco_module_rm.rm-actions + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction + org.alfresco.repo.action.executer.ActionExecuter + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.execute=RM_ALLOW + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.*=RM_ALLOW + org.alfresco.repo.action.executer.ActionExecuter.*=RM_ALLOW + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.execute=RM_CAP.0.rma:filePlanComponent.ApproveRecordsScheduledForCutoff + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.*=RM_ALLOW + org.alfresco.repo.action.executer.ActionExecuter.*=RM_ALLOW + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.execute=RM_CAP.0.rma:filePlanComponent.ApproveRecordsScheduledForCutoff + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.*=RM_ALLOW + org.alfresco.repo.action.executer.ActionExecuter.*=RM_ALLOW + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.execute=RM_CAP.0.rma:filePlanComponent.DestroyRecordsScheduledForDestruction + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.*=RM_ALLOW + org.alfresco.repo.action.executer.ActionExecuter.*=RM_ALLOW + + + + + + + + + + + ${rm.ghosting.enabled} + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.execute=RM_ALLOW + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.*=RM_ALLOW + org.alfresco.repo.action.executer.ActionExecuter.*=RM_ALLOW + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.execute=RM_CAP.0.rma:filePlanComponent.ReOpenFolders + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.*=RM_ALLOW + org.alfresco.repo.action.executer.ActionExecuter.*=RM_ALLOW + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.execute=RM_CAP.0.rma:filePlanComponent.CloseFolders + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.*=RM_ALLOW + org.alfresco.repo.action.executer.ActionExecuter.*=RM_ALLOW + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.execute=RM_CAP.0.rma:filePlanComponent.CycleVitalRecords + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.*=RM_ALLOW + org.alfresco.repo.action.executer.ActionExecuter.*=RM_ALLOW + + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.execute=RM_ALLOW + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.*=RM_ALLOW + org.alfresco.repo.action.executer.ActionExecuter.*=RM_ALLOW + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.execute=RM.Declare.0 + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.*=RM_ALLOW + org.alfresco.repo.action.executer.ActionExecuter.*=RM_ALLOW + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.execute=RM_CAP.0.rma:filePlanComponent.UndeclareRecords + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.*=RM_ALLOW + org.alfresco.repo.action.executer.ActionExecuter.*=RM_ALLOW + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.execute=RM_CAP.0.rma:filePlanComponent.ExtendRetentionPeriodOrFreeze + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.*=RM_ALLOW + org.alfresco.repo.action.executer.ActionExecuter.*=RM_ALLOW + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.execute=RM_CAP.0.rma:filePlanComponent.Unfreeze + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.*=RM_ALLOW + org.alfresco.repo.action.executer.ActionExecuter.*=RM_ALLOW + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.execute=RM_CAP.0.rma:filePlanComponent.ViewUpdateReasonsForFreeze + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.*=RM_ALLOW + org.alfresco.repo.action.executer.ActionExecuter.*=RM_ALLOW + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.execute=RM_CAP.0.rma:filePlanComponent.PlanningReviewCycles + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.*=RM_ALLOW + org.alfresco.repo.action.executer.ActionExecuter.*=RM_ALLOW + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.execute=RM_CAP.0.rma:filePlanComponent.ManuallyChangeDispositionDates + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.*=RM_ALLOW + org.alfresco.repo.action.executer.ActionExecuter.*=RM_ALLOW + + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.execute=RM_ALLOW + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.*=RM_ALLOW + org.alfresco.repo.action.executer.ActionExecuter.*=RM_ALLOW + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.execute=RM_ALLOW + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.*=RM_ALLOW + org.alfresco.repo.action.executer.ActionExecuter.*=RM_ALLOW + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.execute=RM_CAP.0.rma:filePlanComponent.AddModifyEventDates + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.*=RM_ALLOW + org.alfresco.repo.action.executer.ActionExecuter.*=RM_ALLOW + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.execute=RM_CAP.0.rma:filePlanComponent.AddModifyEventDates + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.*=RM_ALLOW + org.alfresco.repo.action.executer.ActionExecuter.*=RM_ALLOW + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.execute=RM_CAP.0.rma:filePlanComponent.AuthorizeAllTransfers + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.*=RM_ALLOW + org.alfresco.repo.action.executer.ActionExecuter.*=RM_ALLOW + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.execute=RM_CAP.0.rma:filePlanComponent.AuthorizeNominatedTransfers + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.*=RM_ALLOW + org.alfresco.repo.action.executer.ActionExecuter.*=RM_ALLOW + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.execute=RM_CAP.0.rma:filePlanComponent.CreateModifyDestroyFileplanMetadata + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.*=RM_ALLOW + org.alfresco.repo.action.executer.ActionExecuter.*=RM_ALLOW + + + + + + + + + + \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-actions.properties b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-actions.properties new file mode 100644 index 0000000000..47a031717d --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-actions.properties @@ -0,0 +1,88 @@ +# Disposition Actions + +cutoff.title=Cutoff +cutoff.description=Cutoff + +retain.title=Retain +retain.description=Retain + +destroy.title=Destroy +destroy.description=Destroy + +# Records Management Actions + +file.title=File +file.description=File + +reviewed.title=Reviewed +reviewed.description=Reviewed + +openRecordFolder.title=Open Folder +openRecordFolder.description=Open Folder + +closeRecordFolder.title=Close Folder +closeRecordFolder.description=Close Folder + +setupRecordFolder.title=Setup Folder +setupRecordFolder.description=Setup Folder + +declareRecord.title=Declare Record +declareRecord.description=Declare Record + +undeclareRecord.title=Undeclare Record +undeclareRecord.description=Undeclare Record + +freeze.title=Freeze +freeze.description=Freeze + +unfreeze.title=Unfreeze +unfreeze.description=Unfreeze + +relinquishHold.title=Relinquish Hold +relinquishHold.description=Relinquish Hold + +broadcastVitalRecordDefinition.title=Broadcast Vital Record Definition +broadcastVitalRecordDefinition.description=Broadcast Vital Record Definition + +broadcastDispositionActionDefinitionUpdate.title=Broadcast Disposition Action Definition Update +broadcastDispositionActionDefinitionUpdate.description=Broadcast Disposition Action Definition Update + + +completeEvent.title=Complete Event +completeEvent.description=Complete Event + +undoEvent.title=Undo Event +undoEvent.description=Undo Event + +applyScannedRecord.title=Apply Scanned Record +applyScannedRecord.description=Apply Scanned Record + +applyPdfRecord.title=Apply PDF Record +applyPdfRecord.description=Apply PDF Record + +applyDigitalPhotographRecord.title=Apply Digital Photograph Record +applyDigitalPhotographRecord.description=Apply Digital Photograph Record + +applyWebRecord.title=Apply Web Record +applyWebRecord.description=Apply Web Record + +splitEmail.title=Split Email Attatchments +splitEmail.description=Split email attachments into separate records. + +transfer.title=Transfer +transfer.description=Transfer + +accession.title=Accession +accession.description=Accession + +editHoldReason.title=Edit Hold Reason +editHoldReason.description=Edit Hold Reason + +editReviewAsOfDate.title=Edit Review Date +editReviewAsOfDate.description=Edit Review Date + +editDispositionActionAsOfDate.title=Edit Disposition Action As Of Date +editDispositionActionAsOfDate.description=Edit Disposition Action As Of Date + +createDispositionSchedule.title=Create Disposition Schedule +createDispositionSchedule.description=Create Disposition Schedule diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-capabilities-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-capabilities-context.xml new file mode 100644 index 0000000000..65cd9ea80c --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-capabilities-context.xml @@ -0,0 +1,880 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + RECORD_FOLDER + RECORD + + + + + + + + + + + + + + + + + RECORD_FOLDER + RECORD + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + RECORD_FOLDER + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FILE_PLAN + RECORD_CATEGORY + DISPOSITION_SCHEDULE + + + + + + + + + + + + + + + + + + + + RECORD_CATEGORY + RECORD_FOLDER + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + RECORD_FOLDER + RECORD + + + + + + + + + + + + + + + + + + RECORD_FOLDER + RECORD + + + + + + + + + + + + + + + + + + + + + + + + RECORD + + + + + + + + + + + + + + + + + + + RECORD + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + RECORD + + + + + + + + + + + + + + + RECORD_FOLDER + RECORD + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + RECORD + + + + + + + + + + + + + + + + + + + + + + + + + + + + + RECORD + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + RECORD_FOLDER + RECORD + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + RECORD_FOLDER + + + + + + + + + + + + + + + + + + + + + + + RECORD_FOLDER + RECORD + + + + + + + + + + + + + + + + + RECORD + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + RECORD + + + + + + + + + + + + + + \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-events.properties b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-events.properties new file mode 100644 index 0000000000..354503dbdd --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-events.properties @@ -0,0 +1,5 @@ +# Event Types +rmeventservice.rmEventType.simple=Simple Event +rmeventservice.rmEventType.obsolete=Obsoleted Event +rmeventservice.rmEventType.superseded=Superseded Event +rmeventservice.rmEventType.crossReferencedRecordTransfered=Cross Referenced Record Transfered \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-id-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-id-context.xml new file mode 100644 index 0000000000..fd1b8662a6 --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-id-context.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-job-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-job-context.xml new file mode 100644 index 0000000000..80d7c95f95 --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-job-context.xml @@ -0,0 +1,134 @@ + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.job.NotifyOfRecordsDueForReviewJob + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 0/15 * * * ? + + + + + + + org.alfresco.module.org_alfresco_module_rm.job.DispositionLifecycleJob + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 0/15 * * * ? + + + + + + org.alfresco.module.org_alfresco_module_rm.job.PublishUpdatesJob + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0/30 * * * * ? + + + + + + + + + + + + + \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml new file mode 100644 index 0000000000..a882c2d8d1 --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml @@ -0,0 +1,104 @@ + + + + + + + + + + alfresco/module/org_alfresco_module_rm/model/recordsModel.xml + + + + + alfresco/module/org_alfresco_module_rm/messages/records-model + + + + + + /app:company_home/app:dictionary/cm:records_management + + + path + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-patch-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-patch-context.xml new file mode 100644 index 0000000000..faf01ef9d4 --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-patch-context.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-public-services-security-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-public-services-security-context.xml new file mode 100644 index 0000000000..4eecb97413 --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-public-services-security-context.xml @@ -0,0 +1,922 @@ + + + + + + + + + + + + + + + + + + + + + alfresco/model/permissionDefinitions.xml + + + alfresco/model/permissionSchema.dtd + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {http://www.alfresco.org/model/recordsmanagement/1.0}filePlanComponent + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${system.acl.maxPermissionCheckTimeMillis} + + + ${system.acl.maxPermissionChecks} + + + + {http://www.alfresco.org/model/recordsmanagement/1.0}filePlanComponent + + + + + + + ${system.acl.maxPermissionCheckTimeMillis} + + + ${system.acl.maxPermissionChecks} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml new file mode 100644 index 0000000000..f52e7a188d --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml @@ -0,0 +1,1015 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.RecordsManagementService + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordService + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchService + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + UpdateProperties + ManageAccessRights + Delete + AccessAudit + CycleVitalRecords + ApproveRecordsScheduledForCutoff + DestroyRecordsCapability + DestroyRecordsScheduledForDestruction + AuthorizeAllTransfers + AuthorizeNominatedTransfers + CreateModifyRecordsInCutoffFolders + ManuallyChangeDispositionDates + PlanningReviewCycles + UndeclareRecords + Declare + Unfreeze + ViewUpdateReasonsForFreeze + CloseFolders + ReOpenFolders + ExtendRetentionPeriodOrFreeze + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + rma:recordCategory + rma:recordFolder + rma:record + rma:nonElectronicDocument + + + + + + + org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditService + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.email.CustomEmailMappingService + + + + customEmailMappingService + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {http://www.alfresco.org/model/recordsmanagement/1.0}recordComponentIdentifier + + + + + + + + {http://www.alfresco.org/model/rmcustom/1.0}rmcustom + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + + org.alfresco.module.org_alfresco_module_rm.caveat.RMCaveatConfigService + + + + caveatConfigService + + + + + + + + + + + + + + + + {http://www.alfresco.org/model/recordsmanagement/1.0}cutOffDate + {http://www.alfresco.org/model/recordsmanagement/1.0}dispositionAsOf + {http://www.alfresco.org/model/recordsmanagement/1.0}dateFiled + {http://www.alfresco.org/model/recordsmanagement/1.0}publicationDate + + + + + \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-ui-evaluators-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-ui-evaluators-context.xml new file mode 100644 index 0000000000..af7c65905d --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-ui-evaluators-context.xml @@ -0,0 +1,614 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + RECORD_FOLDER + + + + + + + + + + RECORD_FOLDER + + + + + + + + + RECORD + + + + + + + + + RECORD_FOLDER + + + + + + + + + RECORD_FOLDER + RECORD + + + + + + + + + RECORD_FOLDER + RECORD + + + + + + + + + + RECORD_FOLDER + RECORD + + + + + + + + + + RECORD_FOLDER + RECORD + + + + + + + + + + RECORD_FOLDER + RECORD + + + + + + + + + + RECORD_FOLDER + RECORD + + + + + + + + + RECORD_FOLDER + RECORD + + + + + + + + + + RECORD_FOLDER + RECORD + + + + + + + + + + RECORD + + + + + + + + + + RECORD + + + + + + + + + + + RECORD_CATEGORY + RECORD_FOLDER + RECORD + + + + + + + + + + RECORD_CATEGORY + RECORD_FOLDER + RECORD + + + + + + + + + + RECORD_CATEGORY + RECORD_FOLDER + RECORD + + + + + + + + + + RECORD_CATEGORY + RECORD_FOLDER + RECORD + + + + + + + + + + RECORD_FOLDER + + + + + + + + + + RECORD_FOLDER + + + + + + + + + + RECORD_FOLDER + + + + + + + + + + + RECORD_FOLDER + RECORD + + + + + + + + + + RECORD_FOLDER + RECORD + + + + + + + + + + RECORD_FOLDER + RECORD + + + + + + + + + + RECORD_FOLDER + RECORD + + + + + + + + + + + RECORD_FOLDER + RECORD + + + + + + + + + + RECORD_FOLDER + RECORD + + + + + + + + + + RECORD_FOLDER + RECORD + + + + + + + + + + RECORD_FOLDER + RECORD + + + + + + + + + + RECORD_FOLDER + RECORD + + + + + + + + + RECORD_FOLDER + + + + + + + + + + RECORD + + + + + + + + + + RECORD + + + + + + + + + + RECORD + + + + + + + + + + RECORD + + + + + + + + + + FILE_PLAN + RECORD_CATEGORY + + + + + + + + + + RECORD_CATEGORY + + + + + + + + + + + + + + + + + + + + HOLD + + + + + + + + + + HOLD + + + + + + + + + + + + + + + + + + TRANSFER + + + + + + + + + + TRANSFER + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + RECORD + + + + + + + + + + RECORD + + + + + + + + + + RECORD + + + + + + + + + + RECORD + + + + + diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-webscript-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-webscript-context.xml new file mode 100644 index 0000000000..0b06db0dcb --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-webscript-context.xml @@ -0,0 +1,344 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/security/rm-default-roles-bootstrap.json b/rm-server/config/alfresco/module/org_alfresco_module_rm/security/rm-default-roles-bootstrap.json new file mode 100644 index 0000000000..65687c61d7 --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/security/rm-default-roles-bootstrap.json @@ -0,0 +1,187 @@ +[ + { + "name" : "User", + "displayLabel" : "Records Management User", + "isAdmin" : false, + "capabilities" : + [ + "DeclareRecords", + "ViewRecords" + ] + }, + { + "name" : "PowerUser", + "displayLabel" : "Records Management Power User", + "isAdmin" : false, + "capabilities" : + [ + "DeclareRecords", + "ViewRecords", + "CreateModifyDestroyFolders", + "EditRecordMetadata", + "EditNonRecordMetadata", + "AddModifyEventDates", + "CloseFolders", + "DeclareRecordsInClosedFolders", + "ReOpenFolders", + "CycleVitalRecords", + "PlanningReviewCycles" + ] + }, + { + "name" : "SecurityOfficer", + "displayLabel" : "Records Management Security Officer", + "isAdmin" : false, + "capabilities" : + [ + "DeclareRecords", + "ViewRecords", + "CreateModifyDestroyFolders", + "EditRecordMetadata", + "EditNonRecordMetadata", + "AddModifyEventDates", + "CloseFolders", + "DeclareRecordsInClosedFolders", + "ReOpenFolders", + "CycleVitalRecords", + "PlanningReviewCycles", + "UpdateClassificationDates", + "CreateModifyDestroyClassificationGuides", + "UpgradeDowngradeAndDeclassifyRecords", + "UpdateExemptionCategories" + ] + }, + { + "name" : "RecordsManager", + "displayLabel" : "Records Management Records Manager", + "isAdmin" : false, + "capabilities" : + [ + "DeclareRecords", + "ViewRecords", + "CreateModifyDestroyFolders", + "EditRecordMetadata", + "EditNonRecordMetadata", + "AddModifyEventDates", + "CloseFolders", + "DeclareRecordsInClosedFolders", + "ReOpenFolders", + "CycleVitalRecords", + "PlanningReviewCycles", + "UpdateTriggerDates", + "CreateModifyDestroyEvents", + "ManageAccessRights", + "MoveRecords", + "ChangeOrDeleteReferences", + "DeleteLinks", + "EditDeclaredRecordMetadata", + "ManuallyChangeDispositionDates", + "ApproveRecordsScheduledForCutoff", + "CreateModifyRecordsInCutoffFolders", + "ExtendRetentionPeriodOrFreeze", + "Unfreeze", + "ViewUpdateReasonsForFreeze", + "DestroyRecordsScheduledForDestruction", + "DestroyRecords", + "UpdateVitalRecordCycleInformation", + "UndeclareRecords", + "DeclareAuditAsRecord", + "DeleteAudit", + "CreateModifyDestroyTimeframes", + "AuthorizeNominatedTransfers", + "EditSelectionLists", + "AuthorizeAllTransfers", + "CreateModifyDestroyFileplanMetadata", + "CreateAndAssociateSelectionLists", + "AttachRulesToMetadataProperties", + "CreateModifyDestroyFileplanTypes", + "CreateModifyDestroyRecordTypes", + "MakeOptionalParametersMandatory", + "MapEmailMetadata", + "DeleteRecords", + "TriggerAnEvent", + "CreateModifyDestroyRoles", + "CreateModifyDestroyUsersAndGroups", + "PasswordControl", + "EnableDisableAuditByTypes", + "SelectAuditMetadata", + "DisplayRightsReport", + "AccessAudit", + "ExportAudit", + "CreateModifyDestroyReferenceTypes", + "UpdateClassificationDates", + "CreateModifyDestroyClassificationGuides", + "UpgradeDowngradeAndDeclassifyRecords", + "UpdateExemptionCategories", + "MapClassificationGuideMetadata" + + ] + }, + { + "name" : "Administrator", + "displayLabel" : "Records Management Administrator", + "isAdmin" : true, + "capabilities" : + [ + "DeclareRecords", + "ViewRecords", + "CreateModifyDestroyFolders", + "EditRecordMetadata", + "EditNonRecordMetadata", + "AddModifyEventDates", + "CloseFolders", + "DeclareRecordsInClosedFolders", + "ReOpenFolders", + "CycleVitalRecords", + "PlanningReviewCycles", + "UpdateTriggerDates", + "CreateModifyDestroyEvents", + "ManageAccessRights", + "MoveRecords", + "ChangeOrDeleteReferences", + "DeleteLinks", + "EditDeclaredRecordMetadata", + "ManuallyChangeDispositionDates", + "ApproveRecordsScheduledForCutoff", + "CreateModifyRecordsInCutoffFolders", + "ExtendRetentionPeriodOrFreeze", + "Unfreeze", + "ViewUpdateReasonsForFreeze", + "DestroyRecordsScheduledForDestruction", + "DestroyRecords", + "UpdateVitalRecordCycleInformation", + "UndeclareRecords", + "DeclareAuditAsRecord", + "DeleteAudit", + "CreateModifyDestroyTimeframes", + "AuthorizeNominatedTransfers", + "EditSelectionLists", + "AuthorizeAllTransfers", + "CreateModifyDestroyFileplanMetadata", + "CreateAndAssociateSelectionLists", + "AttachRulesToMetadataProperties", + "CreateModifyDestroyFileplanTypes", + "CreateModifyDestroyRecordTypes", + "MakeOptionalParametersMandatory", + "MapEmailMetadata", + "DeleteRecords", + "TriggerAnEvent", + "CreateModifyDestroyRoles", + "CreateModifyDestroyUsersAndGroups", + "PasswordControl", + "EnableDisableAuditByTypes", + "SelectAuditMetadata", + "DisplayRightsReport", + "AccessAudit", + "ExportAudit", + "CreateModifyDestroyReferenceTypes", + "UpdateClassificationDates", + "CreateModifyDestroyClassificationGuides", + "UpgradeDowngradeAndDeclassifyRecords", + "UpdateExemptionCategories", + "MapClassificationGuideMetadata", + "ManageAccessControls" + ] + } +] + diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/emailmap.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/emailmap.get.desc.xml new file mode 100644 index 0000000000..0347abaa75 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/emailmap.get.desc.xml @@ -0,0 +1,29 @@ + + Get the custom email property map + + fields are specified with "from" and "to". +
+ Example data. +
+    "mappings":
+    [ 
+       {"from" : "messageTo", "to" : "imap:messageTo" } , 
+       {"from" : "Thread-Index", "to" : "imap:threadIndex" } , 
+       {"from" : "messageFrom", "to" : "imap:messageFrom" } , 
+       {"from" : "messageSubject", "to" : "cm:title" } , 
+       {"from" : "messageSubject", "to" : "imap:messageSubject" } , 
+       {"from" : "messageSubject", "to" : "cm:description" } , 
+       {"from" : "messageCc", "to" : "imap:messageCc" } , 
+       {"from" : "Message-ID", "to" : "imap:messageId" } 
+    ] 
+  
+ ]]> +
+ /api/rma/admin/emailmap + argument + user + required + internal +
\ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/emailmap.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/emailmap.get.json.ftl new file mode 100644 index 0000000000..f38906ee62 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/emailmap.get.json.ftl @@ -0,0 +1,8 @@ +<#import "emailmap.lib.ftl" as emailmapLib/> + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": + <@emailmapLib.emailmapJSON emailmap=emailmap /> +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/emailmap.lib.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/emailmap.lib.ftl new file mode 100644 index 0000000000..d4a297276e --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/emailmap.lib.ftl @@ -0,0 +1,14 @@ +<#-- renders an email map object --> + +<#macro emailmapJSON emailmap> +<#escape x as jsonUtils.encodeJSONString(x)> + { + "mappings": + [ + <#list emailmap as mapping> + {"from": "${mapping.from}", "to": "${mapping.to}" }<#if mapping_has_next>, + + ] + } + + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/emailmap.post.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/emailmap.post.desc.xml new file mode 100644 index 0000000000..eec6580c60 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/emailmap.post.desc.xml @@ -0,0 +1,40 @@ + + Update email property map + + Data is specified in JSON format as a JSONObject with two optional fields, "add" and "delete". +
+ The contents of the add array are added. +
+ The contents of the delete array are deleted. +
+ Add mapping: +
+   {
+      "add":
+      [
+         {"to":"rmc:Wibble", "from":"whatever"},
+         {"to":"rmc:wobble", "from":"whatever"}
+      ]
+   }
+  
+ Delete mapping: +
+   {
+      "delete":
+      [
+         {"to":"rmc:Wibble", "from":"whatever"},
+         {"to":"rmc:wobble", "from":"whatever"}
+      ]
+   }
+  
+ Returns data in the same format as the get method + ]]> +
+ /api/rma/admin/emailmap + argument + user + required + internal +
\ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/emailmap.post.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/emailmap.post.json.ftl new file mode 100644 index 0000000000..68bf020fe0 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/emailmap.post.json.ftl @@ -0,0 +1,7 @@ +<#import "emailmap.lib.ftl" as emailmapLib/> + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": <@emailmapLib.emailmapJSON emailmap=emailmap /> +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/emailmap.put.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/emailmap.put.desc.xml new file mode 100644 index 0000000000..eec6580c60 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/emailmap.put.desc.xml @@ -0,0 +1,40 @@ + + Update email property map + + Data is specified in JSON format as a JSONObject with two optional fields, "add" and "delete". +
+ The contents of the add array are added. +
+ The contents of the delete array are deleted. +
+ Add mapping: +
+   {
+      "add":
+      [
+         {"to":"rmc:Wibble", "from":"whatever"},
+         {"to":"rmc:wobble", "from":"whatever"}
+      ]
+   }
+  
+ Delete mapping: +
+   {
+      "delete":
+      [
+         {"to":"rmc:Wibble", "from":"whatever"},
+         {"to":"rmc:wobble", "from":"whatever"}
+      ]
+   }
+  
+ Returns data in the same format as the get method + ]]> +
+ /api/rma/admin/emailmap + argument + user + required + internal +
\ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/emailmap.put.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/emailmap.put.json.ftl new file mode 100644 index 0000000000..99d37fbb76 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/emailmap.put.json.ftl @@ -0,0 +1,8 @@ +<#import "emailmap.lib.ftl" as emailmapLib/> + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": + <@emailmapLib.emailmapJSON emailmap=emailmap /> +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.delete.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.delete.desc.xml new file mode 100644 index 0000000000..0dd4d5018f --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.delete.desc.xml @@ -0,0 +1,13 @@ + + Delete an RM Constraint list + + + + /api/rma/admin/rmconstraints/{listName} + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.delete.js b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.delete.js new file mode 100644 index 0000000000..3863fdf3b5 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.delete.js @@ -0,0 +1,27 @@ +/** + * Delete the rm constraint list + */ +function main() +{ + // Get the shortname + var shortName = url.extension; + + // Get the constraint + var constraint = caveatConfig.getConstraint(shortName); + + if (constraint != null) + { + caveatConfig.deleteConstraintList(shortName); + + // Pass the constraint name to the template + model.constraintName = shortName; + } + else + { + // Return 404 + status.setCode(404, "Constraint List " + shortName + " does not exist"); + return; + } +} + +main(); \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.delete.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.delete.json.ftl new file mode 100644 index 0000000000..71ff31ba48 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.delete.json.ftl @@ -0,0 +1,5 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": { } +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.get.desc.xml new file mode 100644 index 0000000000..db15d75311 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.get.desc.xml @@ -0,0 +1,22 @@ + + Get a RM Constraint method + + + Constraint object +
+
constraintName
the name of the constraint. The underscore character is used instead of the colon
+
constraintTitle
the title of the constraint (human readable)
+
caseSensitive
Are the values case sensitive
+
allowedValues
array of allowed values, this is the complete unrestricted list of all values
+
+ ]]> +
+ /api/rma/admin/rmconstraints/{listName} + argument + user + required + internal +
\ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.get.js b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.get.js new file mode 100644 index 0000000000..3abedfd921 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.get.js @@ -0,0 +1,25 @@ +/** + * Get the detail of the rm constraint + */ +function main() +{ + // Get the shortname + var shortName = url.extension; + + // Get the constraint + var constraint = caveatConfig.getConstraint(shortName); + + if (constraint != null) + { + // Pass the constraint detail to the template + model.constraint = constraint; + } + else + { + // Return 404 + status.setCode(404, "Constraint List " + shortName + " does not exist"); + return; + } +} + +main(); \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.get.json.ftl new file mode 100644 index 0000000000..d01eb83470 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.get.json.ftl @@ -0,0 +1,8 @@ +<#import "rmconstraint.lib.ftl" as rmconstraintLib/> + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": <@rmconstraintLib.constraintJSON constraint=constraint /> + +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.lib.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.lib.ftl new file mode 100644 index 0000000000..f565ec5414 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.lib.ftl @@ -0,0 +1,61 @@ +<#-- renders an rm constraint object --> + +<#macro constraintSummaryJSON constraint> +<#escape x as jsonUtils.encodeJSONString(x)> + { + "url" : "${url.serviceContext + "/api/rma/admin/rmconstraints/" + constraint.name}", + "constraintName" : "${constraint.name}", + "constraintTitle" : "${constraint.title}" + } + + + +<#macro constraintJSON constraint> +<#escape x as jsonUtils.encodeJSONString(x)> + { + "url" : "${url.serviceContext + "/api/rma/admin/rmconstraints/" + constraint.name}", + "constraintName" : "${constraint.name}", + "caseSensitive" : "${constraint.caseSensitive?string("true", "false")}", + "constraintTitle" : "${constraint.title}", + "allowedValues" : [ <#list constraint.allowedValues as allowedValue> "${allowedValue}" <#if allowedValue_has_next>, ] + } + + + +<#macro constraintWithValuesJSON constraint> +<#escape x as jsonUtils.encodeJSONString(x)> + { + "url" : "${url.serviceContext + "/api/rma/admin/rmconstraints/" + constraint.name}", + "constraintName" : "${constraint.name}", + "caseSensitive" : "${constraint.caseSensitive?string("true", "false")}", + "constraintTitle" : "${constraint.title}", + "values" : [ + <#list constraint.values as value> + { + "url" : "${url.serviceContext + "/api/rma/admin/rmconstraints/" + constraint.name + "/values/" + value.valueName}", + "valueName":"${value.valueName}", + "valueTitle":"${value.valueTitle}", + "authorities" : [ <#list value.authorities as authority> { "authorityName" : "${authority.authorityName}", "authorityTitle" : "${authority.authorityTitle}"} <#if authority_has_next>,] + }<#if value_has_next>, + + ] + } + + + +<#macro constraintWithValueJSON constraint value> +<#escape x as jsonUtils.encodeJSONString(x)> + { + "url" : "${url.serviceContext + "/api/rma/admin/rmconstraints/" + constraint.name + "/values/" + value.valueName}", + "constraintName" : "${constraint.name}", + "constraintTitle" : "${constraint.title}", + "value" : + { + "url" : "${url.serviceContext + "/api/rma/admin/rmconstraints/" + constraint.name + "/values/" + value.valueName}", + "valueName":"${value.valueName}", + "valueTitle":"${value.valueTitle}", + "authorities" : [ <#list value.authorities as authority> { "authorityName" : "${authority.authorityName}", "authorityTitle" : "${authority.authorityTitle}"} <#if authority_has_next>,] + } + } + + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.put.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.put.desc.xml new file mode 100644 index 0000000000..b7e4533b8f --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.put.desc.xml @@ -0,0 +1,19 @@ + + Update an RM Constraint List + + The following properties may be updatedConstraint object +
+
+
constraintTitle
Optional, the title of the constraint (human readable)
+
allowedValues
Optional, array of allowed values, the complete list must be specified
+
+ ]]> +
+ /api/rma/admin/rmconstraints/{listName} + argument + user + required + internal +
\ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.put.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.put.json.ftl new file mode 100644 index 0000000000..b572b81729 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.put.json.ftl @@ -0,0 +1,7 @@ +<#import "rmconstraint.lib.ftl" as rmconstraintLib/> + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": <@rmconstraintLib.constraintJSON constraint=constraint /> +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.put.json.js b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.put.json.js new file mode 100644 index 0000000000..263e7ef7d8 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraint.put.json.js @@ -0,0 +1,51 @@ +/** + * Update an rm constraint + */ +function main() +{ + // Get the shortname + var shortName = url.extension; + + // Get the constraint + var constraint = caveatConfig.getConstraint(shortName); + + if (constraint != null) + { + var allowedValues + var title = null; + + if (json.has("constraintTitle")) + { + title = json.get("constraintTitle"); + constraint.updateTitle(title); + } + + if (json.has("allowedValues")) + { + values = json.getJSONArray("allowedValues"); + + var i = 0; + allowedValues = new Array(); + + if (values != null) + { + for (var x = 0; x < values.length(); x++) + { + allowedValues[i++] = values.get(x); + } + } + constraint.updateAllowedValues(allowedValues); + } + + // Pass the constraint detail to the template + model.constraint = constraint; + } + else + { + // Return 404 + status.setCode(404, "Constraint List " + shortName + " does not exist"); + return; + } +} + +main(); \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraints.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraints.get.desc.xml new file mode 100644 index 0000000000..e8dff744df --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraints.get.desc.xml @@ -0,0 +1,18 @@ + + Get the names of all RM Constraint Lists + +
+
constraintTitle
Human readable title for the custom constraint list
+
constraintName
the name of the constraint list, prefixed
+
url
+
+ ]]> +
+ /api/rma/admin/rmconstraints?withEmptyLists={withEmptyLists?} + argument + user + required + internal +
\ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraints.get.js b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraints.get.js new file mode 100644 index 0000000000..b77cdd6eb1 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraints.get.js @@ -0,0 +1,19 @@ +/** + * List the names of the rm constraints + */ +function main() +{ + var wel = true; + var withEmptyLists = args["withEmptyLists"]; + // Pass the information to the template + if (withEmptyLists != null && withEmptyLists === 'false') + { + model.constraints = caveatConfig.constraintsWithoutEmptyList; + } + else + { + model.constraints = caveatConfig.allConstraints; + } +} + +main(); \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraints.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraints.get.json.ftl new file mode 100644 index 0000000000..172e46f633 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraints.get.json.ftl @@ -0,0 +1,13 @@ +<#import "rmconstraint.lib.ftl" as rmconstraintLib/> + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": + [ + <#list constraints as constraint> + <@rmconstraintLib.constraintSummaryJSON constraint=constraint /> + <#if constraint_has_next>, + + ] +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraints.post.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraints.post.desc.xml new file mode 100644 index 0000000000..3d0036b1d4 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraints.post.desc.xml @@ -0,0 +1,20 @@ + + Create a new RM Constraint List + + The following properties may be specified +
+
+
constraintName
Optional the name of the constraint. If not specified then one will be generated.
+
constraintTitle
The title of the constraint (human readable)
+
allowedValues
array of allowed values, the complete list must be specified
+
+ ]]> +
+ /api/rma/admin/rmconstraints + argument + user + required + internal +
\ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraints.post.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraints.post.json.ftl new file mode 100644 index 0000000000..b572b81729 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraints.post.json.ftl @@ -0,0 +1,7 @@ +<#import "rmconstraint.lib.ftl" as rmconstraintLib/> + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": <@rmconstraintLib.constraintJSON constraint=constraint /> +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraints.post.json.js b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraints.post.json.js new file mode 100644 index 0000000000..4c8b5a0804 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/rmconstraints.post.json.js @@ -0,0 +1,47 @@ +/** + * Create a new RM Constraint List + */ +function main() +{ + // Parse the passed in details + var title = null; + var name = null; + var allowedValues = {}; + + if (json.has("allowedValues")) + { + values = json.getJSONArray("allowedValues"); + + var i = 0; + allowedValues = new Array(); + + if (values != null) + { + for (var x = 0; x < values.length(); x++) + { + allowedValues[i++] = values.get(x); + } + } + } + + if (json.has("constraintName")) + { + name = json.get("constraintName"); + } + + if (json.has("constraintTitle")) + { + title = json.get("constraintTitle"); + } + else + { + title = name; + } + + var constraint = caveatConfig.createConstraint(name, title, allowedValues); + + // Pass the constraint detail to the template + model.constraint = constraint; +} + +main(); \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraint.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraint.get.desc.xml new file mode 100644 index 0000000000..e9f2517b1c --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraint.get.desc.xml @@ -0,0 +1,63 @@ + + Get an RM Constraint + + + The list name is qualified with an underscore between prefix and name to be compatible with Java Script and URLs +
+ e.g. rma_listName rather than rma:listName +
+ Constraint object +
+
constraintName
the name of the constraint. The underscore character is used instead of the colon
+
caseSensitive
is the constraint case sensitive
+
constraintTitle
the display name of the constraint
+
allowedValues
array of the allowed values, this is the complete unrestricted list of all values
+
values
array of constraint values
+
+ Constraint values object +
+
valueName
the full name of the value
+
valueTitle
the display name of the value
+
authorities
array of constraint authorities
+
+ Example JSON data +
+   {
+      "data":
+      {
+         "url" : "\/alfresco\/service\/api\/rma\/admin\/rmconstraints\/rma_smList",
+         "constraintName" : "rma_smList",
+         "caseSensitive" :  "true",
+         "constraintTitle" : "Display title for rma:smList",
+         "allowedValues" : [ "Alpha" ,  "Beta" ,  "Gamma" ],
+         "values" :
+         [
+            {
+               "valueName":"NOCON",
+               "valueTitle":"NOCON",
+               "authorities" : [  { "authorityName" : "jrogers", "authorityTitle" : "jrogers"} ]
+            },
+            {
+               "valueName":"NOFORN",
+               "valueTitle":"NOFORN",
+               "authorities" : [  { "authorityName" : "jrogers", "authorityTitle" : "jrogers"} , { "authorityName" : "fbloggs", "authorityTitle" : "fbloggs"} , { "authorityName" : "jdoe", "authorityTitle" : "jdoe"} ]
+            },
+            {
+               "valueName":"FGI",
+               "valueTitle":"FGI",
+               "authorities" : [  { "authorityName" : "fbloggs", "authorityTitle" : "fbloggs"} , { "authorityName" : "jdoe", "authorityTitle" : "jdoe"} ]
+            }
+         ]
+      }
+   }
+   
+ ]]> +
+ /api/rma/admin/rmconstraints/{listName}/values + argument + user + required + internal +
\ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraint.get.js b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraint.get.js new file mode 100644 index 0000000000..74261a660e --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraint.get.js @@ -0,0 +1,25 @@ +/** + * Get the detail of the rm constraint + */ +function main() +{ + var urlElements = url.extension.split("/"); + var shortName = urlElements[0]; + + // Get the constraint + var constraint = caveatConfig.getConstraint(shortName); + + if (constraint != null) + { + // Pass the constraint detail to the template + model.constraint = constraint; + } + else + { + // Return 404 + status.setCode(404, "Constraint List " + shortName + " does not exist"); + return; + } +} + +main(); \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraint.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraint.get.json.ftl new file mode 100644 index 0000000000..ba9688a12b --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraint.get.json.ftl @@ -0,0 +1,7 @@ +<#import "../rmconstraint.lib.ftl" as rmconstraintLib/> + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": <@rmconstraintLib.constraintWithValuesJSON constraint=constraint /> +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraint.post.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraint.post.desc.xml new file mode 100644 index 0000000000..d6c4af865f --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraint.post.desc.xml @@ -0,0 +1,28 @@ + + Updates values in an an RM Constraint List + + If the authority is missing from the list then the value is deleted. +
+ If an authority does not exist in a list then the authority is added. +
+ Only the authorities for the specified values are updated. +
+ If a value is missing it will not be affected. +
+ JSON Parameter format: +
+ "values" : [ ValueName, [ authorityName1, authorityName2 ]] +
+ The input format for values is different to the output format. +
+ Data Return format. + ]]> +
+ /api/rma/admin/rmconstraints/{listName}/values + argument + user + required + internal +
\ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraint.post.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraint.post.json.ftl new file mode 100644 index 0000000000..ba9688a12b --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraint.post.json.ftl @@ -0,0 +1,7 @@ +<#import "../rmconstraint.lib.ftl" as rmconstraintLib/> + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": <@rmconstraintLib.constraintWithValuesJSON constraint=constraint /> +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraint.post.json.js b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraint.post.json.js new file mode 100644 index 0000000000..731432cc78 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraint.post.json.js @@ -0,0 +1,39 @@ +/** + * Update the details of a value in an rm constraint + */ +function main() +{ + var urlElements = url.extension.split("/"); + var shortName = urlElements[0]; + + var values = null; + + if (json.has("values")) + { + values = json.getJSONArray("values"); + } + + if (values == null) + { + status.setCode(status.STATUS_BAD_REQUEST, "Values missing"); + return; + } + + // Get the constraint + var constraint = caveatConfig.getConstraint(shortName); + + if (constraint != null) + { + constraint.updateValues(values); + model.constraint = caveatConfig.getConstraint(shortName); + model.constraintName = shortName; + } + else + { + // Return 404 + status.setCode(404, "Constraint List " + shortName + " does not exist"); + return; + } +} + +main(); \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraintvalue.delete.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraintvalue.delete.desc.xml new file mode 100644 index 0000000000..74b3f1330e --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraintvalue.delete.desc.xml @@ -0,0 +1,11 @@ + + Delete a value from an RM Constraint List + + + /api/rma/admin/rmconstraints/{listName}/{valueName} + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraintvalue.delete.js b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraintvalue.delete.js new file mode 100644 index 0000000000..28d5e19382 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraintvalue.delete.js @@ -0,0 +1,41 @@ +/** + * Delete the rm constraint list + */ +function main() +{ + var urlElements = url.extension.split("/"); + var shortName = urlElements[0]; + var authorityName = urlElements[1]; + + if (shortName == null) + { + status.setCode(status.STATUS_BAD_REQUEST, "shortName missing"); + return; + } + if (valueName == null) + { + status.setCode(status.STATUS_BAD_REQUEST, "value missing"); + return; + } + + // Get the constraint + var constraint = caveatConfig.getConstraint(shortName); + + if (constraint != null) + { + caveatConfig.deleteRMConstraintListValue(shortName, valueName); + + var constraint = caveatConfig.getConstraint(shortName); + + // Pass the constraint name to the template + model.constraint = constraint; + } + else + { + // Return 404 + status.setCode(404, "Constraint List " + shortName + " does not exist"); + return; + } +} + +main(); \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraintvalue.delete.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraintvalue.delete.json.ftl new file mode 100644 index 0000000000..c811deeed9 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraintvalue.delete.json.ftl @@ -0,0 +1,7 @@ +<#import "../rmconstraint.lib.ftl" as rmconstraintLib/> + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": <@rmconstraintLib.constraintWithValuesJSON constraint=constraint /> +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraintvalue.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraintvalue.get.desc.xml new file mode 100644 index 0000000000..482c2ebf21 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraintvalue.get.desc.xml @@ -0,0 +1,47 @@ + + Get an RM Constraint Value + + + The list name is qualified with an underscore between prefix and name to be compatible with Java Script and URLs +
+ e.g. rma_listName rather than rma:listName +
+ Constraint object +
+
constraintName
the name of the constraint. The underscore character is used instead of the colon
+
constraintTitle
the display name of the constraint
+
value
constraint values
+
+ Constraint values object +
+
valueName
the full name of the value
+
valueTitle
the display name of the value
+
authorities
array of constraint authorities
+
+ Example JSON data: +
+   {
+      "data":
+      {
+         "url" : "\/alfresco\/service\/api\/rma\/admin\/rmconstraints\/rma_smList/values/NOCON",
+         "constraintName" : "rma_smList",
+         "constraintTitle" : "Display title for rma:smList",
+         "value" :
+         {
+           "valueName":"NOCON", 
+           "valueTitle":"NOCON",
+           "authorities" : [  { "authorityName" : "jrogers", "authorityTitle" : "jrogers"} ]
+         }
+      }
+   }
+   
+ ]]> +
+ /api/rma/admin/rmconstraints/{listName}/values/{valueName} + argument + user + required + internal +
\ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraintvalue.get.js b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraintvalue.get.js new file mode 100644 index 0000000000..5bf8b82f17 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraintvalue.get.js @@ -0,0 +1,36 @@ +/** + * Get the detail of the rm constraint + */ +function main() +{ + var urlElements = url.extension.split("/"); + var shortName = decodeURIComponent(urlElements[0]); + var valueName = decodeURIComponent(urlElements[2]) + + // Get the constraint + var constraint = caveatConfig.getConstraint(shortName); + + if (constraint != null) + { + // Pass the constraint detail to the template + var value = constraint.getValue(valueName); + + if(value == null) + { + // Return 404 + status.setCode(404, "Constraint List: " + shortName + " value: " + valueName + "does not exist"); + return; + } + + model.value = value; + model.constraint = constraint; + } + else + { + // Return 404 + status.setCode(404, "Constraint List " + shortName + " does not exist"); + return; + } +} + +main(); \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraintvalue.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraintvalue.get.json.ftl new file mode 100644 index 0000000000..392a3b2ac0 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmconstraint/values/rmconstraintvalue.get.json.ftl @@ -0,0 +1,7 @@ +<#import "../rmconstraint.lib.ftl" as rmconstraintLib/> + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": <@rmconstraintLib.constraintWithValueJSON constraint=constraint value=value/> +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevent.delete.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevent.delete.desc.xml new file mode 100644 index 0000000000..0c4801c54a --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevent.delete.desc.xml @@ -0,0 +1,14 @@ + + Deletes a records management event + + + + /api/rma/admin/rmevents/{eventname} + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevent.delete.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevent.delete.json.ftl new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevent.delete.json.ftl @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevent.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevent.get.desc.xml new file mode 100644 index 0000000000..e7e1294a75 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevent.get.desc.xml @@ -0,0 +1,14 @@ + + Get an records management event + + + + /api/rma/admin/rmevents/{eventname} + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevent.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevent.get.json.ftl new file mode 100644 index 0000000000..7d43337683 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevent.get.json.ftl @@ -0,0 +1,8 @@ +<#import "rmevent.lib.ftl" as rmEventLib/> + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": + <@rmEventLib.eventJSON event=event /> +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevent.lib.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevent.lib.ftl new file mode 100644 index 0000000000..53d3e1fff8 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevent.lib.ftl @@ -0,0 +1,12 @@ +<#-- renders an rm event object --> + +<#macro eventJSON event> +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "eventName": "${event.name}", + "eventDisplayLabel": "${event.displayLabel}", + "eventType":"${event.type}" +} + + + diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevent.put.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevent.put.desc.xml new file mode 100644 index 0000000000..3457e2ee65 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevent.put.desc.xml @@ -0,0 +1,14 @@ + + Update a records management event + + + + /api/rma/admin/rmevents/{eventname} + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevent.put.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevent.put.json.ftl new file mode 100644 index 0000000000..7d43337683 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevent.put.json.ftl @@ -0,0 +1,8 @@ +<#import "rmevent.lib.ftl" as rmEventLib/> + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": + <@rmEventLib.eventJSON event=event /> +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevents.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevents.get.desc.xml new file mode 100644 index 0000000000..ca2c548783 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevents.get.desc.xml @@ -0,0 +1,14 @@ + + Get list of records management events + + + + /api/rma/admin/rmevents + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevents.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevents.get.json.ftl new file mode 100644 index 0000000000..ebe9d531a6 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevents.get.json.ftl @@ -0,0 +1,13 @@ +<#import "rmevent.lib.ftl" as rmEventLib/> + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": + { + <#list events as event> + "${event.name}": + <@rmEventLib.eventJSON event=event /><#if event_has_next>, + + } +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevents.post.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevents.post.desc.xml new file mode 100644 index 0000000000..ed0a8207d5 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevents.post.desc.xml @@ -0,0 +1,14 @@ + + Create a new records managment event + + + + /api/rma/admin/rmevents + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevents.post.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevents.post.json.ftl new file mode 100644 index 0000000000..8a9faca5d6 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmevents.post.json.ftl @@ -0,0 +1,9 @@ +<#import "rmevent.lib.ftl" as rmEventLib/> + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": + <@rmEventLib.eventJSON event=event /> + } +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmeventtypes.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmeventtypes.get.desc.xml new file mode 100644 index 0000000000..d14aecb466 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmeventtypes.get.desc.xml @@ -0,0 +1,14 @@ + + Gets the records management event types + + + + /api/rma/admin/rmeventtypes + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmeventtypes.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmeventtypes.get.json.ftl new file mode 100644 index 0000000000..21d6c3a7e3 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmevent/rmeventtypes.get.json.ftl @@ -0,0 +1,16 @@ +<#import "rmevent.lib.ftl" as rmEventLib/> + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": + { + <#list eventtypes as eventtype> + "${eventtype.name}": + { + "eventTypeName" : "${eventtype.name}", + "eventTypeDisplayLabel" : "${eventtype.displayLabel}" + }<#if eventtype_has_next>, + + } +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmrole.delete.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmrole.delete.desc.xml new file mode 100644 index 0000000000..c7b6c75835 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmrole.delete.desc.xml @@ -0,0 +1,14 @@ + + Deletes a records management role + + + + /api/rma/admin/rmroles/{rolename} + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmrole.delete.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmrole.delete.json.ftl new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmrole.delete.json.ftl @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmrole.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmrole.get.desc.xml new file mode 100644 index 0000000000..21693f3819 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmrole.get.desc.xml @@ -0,0 +1,14 @@ + + Get an records management role + + + + /api/rma/admin/rmroles/{rolename} + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmrole.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmrole.get.json.ftl new file mode 100644 index 0000000000..3a4a96b70f --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmrole.get.json.ftl @@ -0,0 +1,8 @@ +<#import "rmrole.lib.ftl" as rmRoleLib/> + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": + <@rmRoleLib.roleJSON role=role /> +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmrole.lib.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmrole.lib.ftl new file mode 100644 index 0000000000..48b4c854d8 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmrole.lib.ftl @@ -0,0 +1,15 @@ +<#-- renders an rm role object --> +<#macro roleJSON role> +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "name": "${role.name}", + "displayLabel": "${role.displayLabel}", + "capabilities" : + [ + <#list role.capabilities as capability> + "${capability}"<#if capability_has_next>, + + ] +} + + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmrole.put.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmrole.put.desc.xml new file mode 100644 index 0000000000..7849806794 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmrole.put.desc.xml @@ -0,0 +1,14 @@ + + Update a records management role + + + + /api/rma/admin/rmroles/{rolename} + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmrole.put.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmrole.put.json.ftl new file mode 100644 index 0000000000..3a4a96b70f --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmrole.put.json.ftl @@ -0,0 +1,8 @@ +<#import "rmrole.lib.ftl" as rmRoleLib/> + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": + <@rmRoleLib.roleJSON role=role /> +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmroles.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmroles.get.desc.xml new file mode 100644 index 0000000000..c3dd070b97 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmroles.get.desc.xml @@ -0,0 +1,14 @@ + + Get list of records management roles + + + + /api/rma/admin/rmroles?user={user?} + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmroles.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmroles.get.json.ftl new file mode 100644 index 0000000000..5cf2281975 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmroles.get.json.ftl @@ -0,0 +1,13 @@ +<#import "rmrole.lib.ftl" as rmRoleLib/> + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": + { + <#list roles as role> + "${role.name}": + <@rmRoleLib.roleJSON role=role /><#if role_has_next>, + + } +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmroles.post.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmroles.post.desc.xml new file mode 100644 index 0000000000..205815899c --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmroles.post.desc.xml @@ -0,0 +1,14 @@ + + Create a new records managment role + + + + /api/rma/admin/rmroles + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmroles.post.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmroles.post.json.ftl new file mode 100644 index 0000000000..3a4a96b70f --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/admin/rmrole/rmroles.post.json.ftl @@ -0,0 +1,8 @@ +<#import "rmrole.lib.ftl" as rmRoleLib/> + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": + <@rmRoleLib.roleJSON role=role /> +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/applydodcertmodelfixes.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/applydodcertmodelfixes.get.desc.xml new file mode 100644 index 0000000000..bcc2921b4e --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/applydodcertmodelfixes.get.desc.xml @@ -0,0 +1,15 @@ + + Applies fixes to the content model for DoD certification. + + Please note that this script will be removed after the certification process is complete. + ]]> + /api/rma/applydodcertmodelfixes + argument + admin + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/applydodcertmodelfixes.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/applydodcertmodelfixes.get.json.ftl new file mode 100644 index 0000000000..53ac8f9883 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/applydodcertmodelfixes.get.json.ftl @@ -0,0 +1,5 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "success": ${success?string} +} + diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/applyfixmob1573.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/applyfixmob1573.get.desc.xml new file mode 100644 index 0000000000..9dada0f840 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/applyfixmob1573.get.desc.xml @@ -0,0 +1,9 @@ + + Applies fix for MOB-1573. + Fixes the RM custom model by changing the multiplicity on custom references to many-to-many. + /api/rma/applyfixmob1573 + argument + admin + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/applyfixmob1573.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/applyfixmob1573.get.json.ftl new file mode 100644 index 0000000000..53ac8f9883 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/applyfixmob1573.get.json.ftl @@ -0,0 +1,5 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "success": ${success?string} +} + diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/bootstraptestdata.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/bootstraptestdata.get.desc.xml new file mode 100644 index 0000000000..18aa65c8ba --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/bootstraptestdata.get.desc.xml @@ -0,0 +1,9 @@ + + Load the RM DOD bootstrap data. + WebScript to import and fix up the RM DOD bootstrap data. + /api/rma/bootstraptestdata?site={site?}&import={import?} + argument + admin + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/bootstraptestdata.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/bootstraptestdata.get.json.ftl new file mode 100644 index 0000000000..53ac8f9883 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/bootstraptestdata.get.json.ftl @@ -0,0 +1,5 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "success": ${success?string} +} + diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customisable.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customisable.get.desc.xml new file mode 100644 index 0000000000..384249493f --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customisable.get.desc.xml @@ -0,0 +1,12 @@ + + Records Management Customisable Types and Aspects + + + /api/rma/admin/customisable + + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customisable.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customisable.get.json.ftl new file mode 100644 index 0000000000..6009a0eb91 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customisable.get.json.ftl @@ -0,0 +1,14 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": + [ + <#list items as item> + { + "name" : "${item.name}", + "isAspect" : ${item.isAspect?string}, + "title" : "${item.title}" + }<#if item_has_next>, + + ] +} + diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/custompropdefinition.delete.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/custompropdefinition.delete.desc.xml new file mode 100644 index 0000000000..90aa0b9af9 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/custompropdefinition.delete.desc.xml @@ -0,0 +1,18 @@ + + Attempts to remove specified Custom Property Definitions from the custom model + + It should be noted that it may not be possible to honour this request in all cases.
+ In cases where instances of the specified property are already present in the system,
+ the request will not succeed. In cases where there are no instances of the specified
+ property in the system, the request will attempted.
+
+ The propId is that returned by custompropertydefinitions.get. + ]]> +
+ /api/rma/admin/custompropertydefinitions/{propId} + argument + user + required + internal +
diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/custompropdefinition.delete.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/custompropdefinition.delete.json.ftl new file mode 100644 index 0000000000..f23634ccec --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/custompropdefinition.delete.json.ftl @@ -0,0 +1,8 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": + { + "customProperty": "${propertyqname}" + } +} + diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/custompropdefinition.post.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/custompropdefinition.post.desc.xml new file mode 100644 index 0000000000..9d5af56a68 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/custompropdefinition.post.desc.xml @@ -0,0 +1,29 @@ + + Add a Custom Property Definition to the custom model + +
+ The URL query parameter 'element' defines which RM type will be able to have the property added.
+ It should be a the customisable types short qname type (eg rma:recordCategory)
+
+ The JSON parameter 'propId' is optional. If a value is provided it must only contain characters
+ which are legal within URLs and within QNames.
+ It is also the responsibility of the calling code to ensure the propId is unique across all custom properties.
+ If a value is not provided, one will be generated.
+
+ The body of the post should be in the form, e.g.
+ {
+    "label": "sample Custom Property",
+    "dataType": "d:boolean",
+    "mandatory": false
, +    "constraintRef": "rmc:constraintName",
+    "propId": "myPropId"
+ }
+ ]]> +
+ /api/rma/admin/custompropertydefinitions?element={element} + argument + user + required + internal +
diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/custompropdefinition.post.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/custompropdefinition.post.json.ftl new file mode 100644 index 0000000000..ef996b6013 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/custompropdefinition.post.json.ftl @@ -0,0 +1,6 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "propId": "${propId}", + "url": "${url}" +} + diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/custompropdefinition.put.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/custompropdefinition.put.desc.xml new file mode 100644 index 0000000000..f4dcf82e60 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/custompropdefinition.put.desc.xml @@ -0,0 +1,25 @@ + + Updates a Custom Property Definition. + +
+ There is currently support only for updating the label and/or for updating the constraint.
+ The body of the PUT should be in the form, e.g.
+ {
+    "label": "updated label value",
+    "constraintRef": "rmc:constraintName",
+ }
+ In the above example JSON, a constraintRef with QName "rmc:constraintName" will be added to the
+ property definition if one does not exist. If there already is a constraint, it will be replaced.
+ It is also possible to remove all constraints from the property definition by passing null:
+ {
+    "constraintRef": null,
+ }
+ ]]> +
+ /api/rma/admin/custompropertydefinitions/{propId} + argument + user + required + internal +
diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/custompropdefinition.put.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/custompropdefinition.put.json.ftl new file mode 100644 index 0000000000..ef996b6013 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/custompropdefinition.put.json.ftl @@ -0,0 +1,6 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "propId": "${propId}", + "url": "${url}" +} + diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/custompropdefinitions.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/custompropdefinitions.get.desc.xml new file mode 100644 index 0000000000..bd6b7b218c --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/custompropdefinitions.get.desc.xml @@ -0,0 +1,14 @@ + + Records Management Custom Model Property Definitions + + If a propId is specified within the URL, only that specific property definition is returned.
+ ]]> +
+ /api/rma/admin/custompropertydefinitions?element={element} + /api/rma/admin/custompropertydefinitions/{propId} + + user + required + internal +
\ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/custompropdefinitions.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/custompropdefinitions.get.json.ftl new file mode 100644 index 0000000000..bbdee48371 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/custompropdefinitions.get.json.ftl @@ -0,0 +1,43 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": + { + "customProperties": + { + <#list customProps as prop> + "${prop.name.toPrefixString()}": + { + "dataType": "<#if prop.dataType??>${prop.dataType.name.toPrefixString()}", + "label": "${prop.title!""}", + "description": "${prop.description!""}", + "mandatory": ${prop.mandatory?string}, + "multiValued": ${prop.multiValued?string}, + "defaultValue": "${prop.defaultValue!""}", + "protected": ${prop.protected?string}, + "propId": "${prop.name.localName}", + "constraintRefs": + [ + <#list prop.constraints as con> + { + "name": "${con.constraint.shortName!""}", + "title": "${con.title!""}", + "type": "${con.constraint.type!""}", + "parameters": + { + <#-- Basic implementation. Only providing 2 hardcoded parameters. --> + <#assign lov = con.constraint.parameters["allowedValues"]> + "caseSensitive": ${con.constraint.parameters["caseSensitive"]?string}, + "listOfValues" : + [ + <#list lov as val>"${val}"<#if val_has_next>, + ] + } + }<#if con_has_next>, + + ] + }<#if prop_has_next>, + + } + } +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customref.delete.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customref.delete.desc.xml new file mode 100644 index 0000000000..464533568d --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customref.delete.desc.xml @@ -0,0 +1,14 @@ + + Removes specified Custom Reference Instance from between the specified nodes + + The nodeRef encoded within the URL path is the 'fromNode' in the relationship.
+ The nodeRef encoded within the query string is the 'toNode' in the relationship.
+ ]]> +
+ /api/node/{store_type}/{store_id}/{id}/customreferences/{refId}?st={toStoreType}&si={toStoreId}&id={toId} + argument + user + required + internal +
diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customref.delete.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customref.delete.json.ftl new file mode 100644 index 0000000000..f539b37200 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customref.delete.json.ftl @@ -0,0 +1,3 @@ +{ + "success": ${success?string} +} \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customref.post.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customref.post.desc.xml new file mode 100644 index 0000000000..660c6f63c7 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customref.post.desc.xml @@ -0,0 +1,18 @@ + + Add a Custom Reference instance to the specified record node + +
+ The body of the post should be in the form, e.g.
+ {
+    "toNode" : "workspace://SpacesStore/12345678-abcd-1234-abcd-1234567890ab",
+    "refId" : the refId as returned by customrefdefinitions.get
+ }
+ ]]> +
+ /api/node/{store_type}/{store_id}/{id}/customreferences + argument + user + required + internal +
\ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customref.post.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customref.post.json.ftl new file mode 100644 index 0000000000..1ef9fab7d9 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customref.post.json.ftl @@ -0,0 +1,5 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "success": ${success?string} +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customrefdefinition.post.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customrefdefinition.post.desc.xml new file mode 100644 index 0000000000..d5a793f9ef --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customrefdefinition.post.desc.xml @@ -0,0 +1,22 @@ + + Add a Custom Reference Definition to the custom model + +
+ The body of the post should be in the form, e.g.
+ {
+    "referenceType" : ""parentchild" OR "bidirectional",
+    "label" : "bar"
+    "source" : "foo",
+    "target" : "bar"
+ }
+ For parentchild references, source and target must be provided. For bidirectional references, + a label is required.
+ ]]> +
+ /api/rma/admin/customreferencedefinitions + argument + user + required + internal +
diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customrefdefinition.post.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customrefdefinition.post.json.ftl new file mode 100644 index 0000000000..1216a4579d --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customrefdefinition.post.json.ftl @@ -0,0 +1,10 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "success": ${success?string}, + "data" : { + "referenceType": "${referenceType?string}", + "refId": "${refId?string}", + "url": "${url?string}" + } +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customrefdefinition.put.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customrefdefinition.put.desc.xml new file mode 100644 index 0000000000..45cf4a2301 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customrefdefinition.put.desc.xml @@ -0,0 +1,25 @@ + + Updates a Custom Reference Definition. + +
+ There is currently support only for updating the label, source or target fields.
+
+ The body of the PUT should be in the form, e.g.
+ {
+    "label": "updated label value",
+ }
+ OR + {
+    "source": "updated source value",
+    "target": "updated target value",
+ }
+ for bidirectional and parentchild references respectively.
+ ]]> +
+ /api/rma/admin/customreferencedefinitions/{refId} + argument + user + required + internal +
diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customrefdefinition.put.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customrefdefinition.put.json.ftl new file mode 100644 index 0000000000..ae7ddd675b --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customrefdefinition.put.json.ftl @@ -0,0 +1,6 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "refId": "${refId}", + "url": "${url}" +} + diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customrefdefinitions.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customrefdefinitions.get.desc.xml new file mode 100644 index 0000000000..b474afd1ee --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customrefdefinitions.get.desc.xml @@ -0,0 +1,15 @@ + + Records Management Custom Model Reference Definitions + + If a refId is specified, then only the reference definition corresponding to that
+ id will be returned.
+ ]]> +
+ /api/rma/admin/customreferencedefinitions + /api/rma/admin/customreferencedefinitions/{refId} + + user + required + internal +
\ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customrefdefinitions.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customrefdefinitions.get.json.ftl new file mode 100644 index 0000000000..d4d7fa9331 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customrefdefinitions.get.json.ftl @@ -0,0 +1,16 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": + { + "customReferences": + [ + <#list customRefs as ref> + { + <#assign keys = ref?keys> + <#list keys as key>"${key}": "${ref[key]}"<#if key_has_next>, + }<#if ref_has_next>, + + ] + } +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customrefs.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customrefs.get.desc.xml new file mode 100644 index 0000000000..ff5c9a054b --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customrefs.get.desc.xml @@ -0,0 +1,45 @@ + + Records Management Custom Reference Instances + + The response will have the form:
+ {
+ "data":
+   {
+   "nodeName": "samplename",
+   "nodeTitle": "sample title",
+   "customReferencesFrom":
+     [
+       {
+       "refId": "09876543-wxyz-0987-wxyz-098765432109",
+       "referenceType": "bidirectional",
+       "label": "BiDi",
+       "targetRef": "workspace://SpacesStore/zyxwvuts-4321-zyxw-4321-zyxwvutsrqpo",
+       "sourceRef": "workspace://SpacesStore/a1a1a1a1-a1a1-a1a1-a1a1-a1a1a1a1a1a1"
+       }
+     ]
+   "customReferencesTo":
+     [
+       {
+       "childRef": "workspace://SpacesStore/12345678-abcd-1234-abcd-123456789012",
+       "refId": "versions",
+       "source": "VersionedBy",
+       "referenceType": "parentchild",
+       "target": "Versions",
+       "parentRef": "workspace://SpacesStore/abcdefgh-1234-abcd-1234-abcdefghijkl"
+       }
+     ]
+   }
+ }
+ The "customReferencesFrom" field gives the references that are from this node i.e. from the node
+ to which the GET was issued. Conversely, the "customReferencesTo" field gives the references that
+ are to this node.
+ For parent/child reference types, the reference goes from the parent to the child.
+ ]]> +
+ /api/node/{store_type}/{store_id}/{id}/customreferences + + user + required + internal +
\ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customrefs.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customrefs.get.json.ftl new file mode 100644 index 0000000000..8f38b39d90 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/customrefs.get.json.ftl @@ -0,0 +1,27 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": + { + "nodeName": "${nodeName!""}", + "nodeTitle": "${nodeTitle!""}", + "customReferencesFrom": + [ + <#list customRefsFrom as ref> + { + <#assign keys = ref?keys> + <#list keys as key>"${key}": "${ref[key]}"<#if key_has_next>, + }<#if ref_has_next>, + + ], + "customReferencesTo": + [ + <#list customRefsTo as ref> + { + <#assign keys = ref?keys> + <#list keys as key>"${key}": "${ref[key]}"<#if key_has_next>, + }<#if ref_has_next>, + + ] + } +} + diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionactiondefinition.delete.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionactiondefinition.delete.desc.xml new file mode 100644 index 0000000000..cd9df1aa22 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionactiondefinition.delete.desc.xml @@ -0,0 +1,9 @@ + + Delete Disposition Action Definition + Deletes a disposition action definition from the collection. + /api/node/{store_type}/{store_id}/{id}/dispositionschedule/dispositionactiondefinitions/{action_def_id} + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionactiondefinition.delete.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionactiondefinition.delete.json.ftl new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionactiondefinition.delete.json.ftl @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionactiondefinition.lib.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionactiondefinition.lib.ftl new file mode 100644 index 0000000000..33df310043 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionactiondefinition.lib.ftl @@ -0,0 +1,17 @@ +<#macro actionJSON action> +<#escape x as jsonUtils.encodeJSONString(x)> + { + "id": "${action.id}", + "url": "${action.url}", + "index": ${action.index}, + "name": "${action.name}", + "label": "${action.label}", + <#if action.description??>"description": "${action.description}", + <#if action.period??>"period": "${action.period}", + <#if action.periodProperty??>"periodProperty": "${action.periodProperty}", + <#if action.location??>"location": "${action.location}", + <#if action.events??>"events": [<#list action.events as event>"${event}"<#if event_has_next>,], + "eligibleOnFirstCompleteEvent": ${action.eligibleOnFirstCompleteEvent?string} + } + + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionactiondefinition.put.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionactiondefinition.put.desc.xml new file mode 100644 index 0000000000..66692a049a --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionactiondefinition.put.desc.xml @@ -0,0 +1,21 @@ + + Update Disposition Action Definition + + {
+    "name" : Name of action,
+    "description" : Description of the action definition,
+    "period" : The period of time,
+    "periodProperty" : Model property the period is relative to,
+    "eligibleOnFirstCompleteEvent" : Whether all events have to occur,
+    "events" : [List of event names]
+ }
+ ]]> +
+ /api/node/{store_type}/{store_id}/{id}/dispositionschedule/dispositionactiondefinitions/{action_def_id} + argument + user + required + internal +
\ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionactiondefinition.put.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionactiondefinition.put.json.ftl new file mode 100644 index 0000000000..c9a440057d --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionactiondefinition.put.json.ftl @@ -0,0 +1,5 @@ +<#import "dispositionactiondefinition.lib.ftl" as actionDefLib/> +{ + "data": + <@actionDefLib.actionJSON action=action/> +} \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionactiondefinitions.post.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionactiondefinitions.post.desc.xml new file mode 100644 index 0000000000..a2e5880649 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionactiondefinitions.post.desc.xml @@ -0,0 +1,21 @@ + + Add Disposition Action Definition + + {
+    "name" : Name of action,
+    "description" : Description of the action definition,
+    "period" : The period of time,
+    "periodProperty" : Model property the period is relative to,
+    "eligibleOnFirstCompleteEvent" : Whether all events have to occur,
+    "events" : [List of event names]
+ }
+ ]]> +
+ /api/node/{store_type}/{store_id}/{id}/dispositionschedule/dispositionactiondefinitions + argument + user + required + internal +
\ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionactiondefinitions.post.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionactiondefinitions.post.json.ftl new file mode 100644 index 0000000000..0a0aa2d064 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionactiondefinitions.post.json.ftl @@ -0,0 +1,5 @@ +<#import "dispositionactiondefinition.lib.ftl" as actionDefLib/> +{ + "data": + <@actionDefLib.actionJSON action=action/> +} diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionlifecycle.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionlifecycle.get.desc.xml new file mode 100644 index 0000000000..f80ee5b24b --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionlifecycle.get.desc.xml @@ -0,0 +1,9 @@ + + Get Dispositon Lifecycle + Returns Disposition Lifecycle data + /api/node/{store_type}/{store_id}/{id}/nextdispositionaction + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionlifecycle.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionlifecycle.get.json.ftl new file mode 100644 index 0000000000..4836277100 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionlifecycle.get.json.ftl @@ -0,0 +1,35 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": + { + "url": "${nextaction.url}", + "name": "${nextaction.name}", + "label": "${nextaction.label}", + "eventsEligible": ${nextaction.eventsEligible?string}, + <#if nextaction.asOf??>"asOf": "${nextaction.asOf}", + <#if nextaction.startedAt??>"startedAt": "${nextaction.startedAt}", + <#if nextaction.startedBy??>"startedBy": "${nextaction.startedBy}", + <#if nextaction.startedByFirstName??>"startedByFirstName": "${nextaction.startedByFirstName}", + <#if nextaction.startedByLastName??>"startedByLastName": "${nextaction.startedByLastName}", + <#if nextaction.completedAt??>"completedAt": "${nextaction.completedAt}", + <#if nextaction.completedBy??>"completedBy": "${nextaction.completedBy}", + <#if nextaction.completedByFirstName??>"completedByFirstName": "${nextaction.completedByFirstName}", + <#if nextaction.completedByLastName??>"completedByLastName": "${nextaction.completedByLastName}", + "events": + [ + <#list nextaction.events as event> + { + "name": "${event.name}", + "label": "${event.label}", + "complete": ${event.complete?string}, + <#if event.completedAt??>"completedAt": "${event.completedAt}", + <#if event.completedBy??>"completedBy": "${event.completedBy}", + <#if event.completedByFirstName??>"completedByFirstName": "${event.completedByFirstName}", + <#if event.completedByLastName??>"completedByLastName": "${event.completedByLastName}", + "automatic": ${event.automatic?string} + }<#if event_has_next>, + + ] + } +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionschedule.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionschedule.get.desc.xml new file mode 100644 index 0000000000..cc21bc2ec4 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionschedule.get.desc.xml @@ -0,0 +1,9 @@ + + Get Dispositon Schedule + Returns Disposition Schedule + /api/node/{store_type}/{store_id}/{id}/dispositionschedule?inherited={inherited?} + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionschedule.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionschedule.get.json.ftl new file mode 100644 index 0000000000..251d15855c --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionschedule.get.json.ftl @@ -0,0 +1,29 @@ +<#import "dispositionactiondefinition.lib.ftl" as actionDefLib/> + +<@scheduleJSON schedule=schedule/> + +<#macro scheduleJSON schedule> +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": + { + "url": "${schedule.url}", + "nodeRef": "${schedule.nodeRef}", + <#if schedule.authority??>"authority": "${schedule.authority}", + <#if schedule.instructions??>"instructions": "${schedule.instructions}", + "unpublishedUpdates" : ${schedule.unpublishedUpdates?string}, + "publishInProgress" : ${schedule.publishInProgress?string}, + "recordLevelDisposition": ${schedule.recordLevelDisposition?string}, + "canStepsBeRemoved": ${schedule.canStepsBeRemoved?string}, + "actionsUrl": "${schedule.actionsUrl}", + "actions": + [ + <#list schedule.actions as action> + <@actionDefLib.actionJSON action=action/> + <#if action_has_next>, + + ] + } +} + + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dodcustomtypes.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dodcustomtypes.get.desc.xml new file mode 100644 index 0000000000..bb5f689ba8 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dodcustomtypes.get.desc.xml @@ -0,0 +1,12 @@ + + Records Management DOD 5015 Custom Types + + ]]> + + /api/rma/admin/dodcustomtypes + + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dodcustomtypes.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dodcustomtypes.get.json.ftl new file mode 100644 index 0000000000..2a70d90714 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/dodcustomtypes.get.json.ftl @@ -0,0 +1,16 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": + { + "dodCustomTypes": + [ + <#list dodCustomTypes as aspDef> + { + "name": "${aspDef.name.prefixString}", + "title": "${aspDef.title!""}" + }<#if aspDef_has_next>, + + ] + } +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/export.post.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/export.post.desc.xml new file mode 100644 index 0000000000..a9f50f435c --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/export.post.desc.xml @@ -0,0 +1,16 @@ + + Records Management Export + + The body of the post should be in the form
+ {
+    "nodeRefs" : array of nodeRefs to export
+ }
+ ]]> +
+ /api/rma/admin/export + argument + user + required + internal +
\ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/export.post.html.status.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/export.post.html.status.ftl new file mode 100644 index 0000000000..6c0811ac9a --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/export.post.html.status.ftl @@ -0,0 +1,19 @@ + + + Export failure + + +<#if (args.failureCallbackFunction?exists)> + + + + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/fileplanreport.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/fileplanreport.get.desc.xml new file mode 100644 index 0000000000..4f99e04187 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/fileplanreport.get.desc.xml @@ -0,0 +1,13 @@ + + Get Fileplan Report + Returns STATUS_OK (200) + ]]> + + /api/node/{store_type}/{store_id}/{id}/fileplanreport + + user + required + draft_public_api + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/fileplanreport.get.js b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/fileplanreport.get.js new file mode 100644 index 0000000000..87e25f8955 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/fileplanreport.get.js @@ -0,0 +1,188 @@ +/** + * Main entry point for this webscript. + * Builds a nodeRef from the url and creates a records series, category and/or folder + * template model depending on what nodeRef that has been given. + * + * @method main + */ +function main() +{ + // Get the node from the URL + var pathSegments = url.match.split("/"); + var reference = [ url.templateArgs.store_type, url.templateArgs.store_id ].concat(url.templateArgs.id.split("/")); + var node = search.findNode(pathSegments[2], reference); + + // 404 if the node is not found + if (node == null) + { + status.setCode(status.STATUS_NOT_FOUND, "The node could not be found"); + return; + } + + // Get the record series, categories and/or folders + if(node.type == "{http://www.alfresco.org/model/dod5015/1.0}filePlan") + { + var recordSeries = [], + seriesNodes = node.children, + seriesNode; + for (var rsi = 0, rsl = seriesNodes.length; rsi < rsl; rsi++) + { + var seriesNode = seriesNodes[rsi]; + if(seriesNode.type == "{http://www.alfresco.org/model/dod5015/1.0}recordSeries") + { + recordSeries.push(getRecordSeries(seriesNode)); + } + } + recordSeries.sort(sortByName); + model.recordSeries = recordSeries; + } + else if(node.type == "{http://www.alfresco.org/model/dod5015/1.0}recordSeries") + { + var recordSeries = []; + recordSeries.push(getRecordSeries(node)); + model.recordSeries = recordSeries; + } + else if(node.type == "{http://www.alfresco.org/model/dod5015/1.0}recordCategory") + { + var recordCategories = []; + recordCategories.push(getRecordCategory(node, "/" + node.parent.name + "/")); + model.recordCategories = recordCategories; + } + else if(node.type == "{http://www.alfresco.org/model/recordsmanagement/1.0}recordFolder") + { + var recordFolders = []; + var recordCategory = node.parent; + recordFolders.push(getRecordFolder(node, "" + recordCategory.parent.name + "/" + recordCategory.name + "/")); + model.recordFolders = recordFolders; + } +} + +/** + * Sort helper function for objects with names + * + * @method sortByName + * @param obj1 + * @param obj2 + */ +function sortByName(obj1, obj2) +{ + return (obj1.name > obj2.name) ? 1 : (obj1.name < obj2.name) ? -1 : 0; +} + +/** + * Takes a ScriptNode and builds a Record Series template model from it + * + * @method getRecordSeries + * @param seriesNode {ScriptNode} A ScriptNode of type "rma:recordSeries" + */ +function getRecordSeries(seriesNode) +{ + // Create Record Series object + var recordSerie = { + parentPath: "/", + name: seriesNode.name, + identifier: seriesNode.properties["rma:identifier"], + description: seriesNode.properties["description"] + }; + + // Find all Record Categories + var recordCategories = [], + categoryNodes = seriesNode.children, + categoryNode; + for (var rci = 0, rcl = categoryNodes.length; rci < rcl; rci++) + { + categoryNode = categoryNodes[rci]; + if(categoryNode.type == "{http://www.alfresco.org/model/dod5015/1.0}recordCategory") + { + // Create and add Record Category object + recordCategories.push(getRecordCategory(categoryNode, "/" + seriesNode.name + "/")); + } + } + recordCategories.sort(sortByName); + recordSerie.recordCategories = recordCategories; + + // Return Record Series + return recordSerie; +} + +/** + * Takes a ScriptNode and builds a Record Category template model from it + * + * @method getRecordCategory + * @param categoryNode {ScriptNode} A ScriptNode of type "rma:recordCategory" + * @param parentPath {string} The file path starting from the top of the fileplan + */ +function getRecordCategory(categoryNode, parentPath) +{ + // Create Record Category object + var recordCategory = { + parentPath: parentPath, + name: categoryNode.name, + identifier: categoryNode.properties["rma:identifier"], + vitalRecordIndicator: categoryNode.properties["vitalRecordIndicator"], + dispositionAuthority: categoryNode.properties["dispositionAuthority"], + recordFolders: [], + dispositionActions: [] + }; + + // Find all Record Folders & Disposition information + var recordFolders = [], + dispositionActions = [], + categoryChildren = categoryNode.children, + categoryChild, + dispScheduleChildren, + dispScheduleChild; + for (var cci = 0, ccil = categoryChildren.length; cci < ccil; cci++) + { + categoryChild = categoryChildren[cci] + if (categoryChild.type == "{http://www.alfresco.org/model/recordsmanagement/1.0}recordFolder") + { + // Create and add Record Folder object + recordFolders.push(getRecordFolder(categoryChild, parentPath + categoryNode.name + "/")); + } + else if (categoryChild.type == "{http://www.alfresco.org/model/recordsmanagement/1.0}dispositionSchedule") + { + // Get Disposition authority + recordCategory.dispositionAuthority = categoryChild.properties["rma:dispositionAuthority"]; + dispScheduleChildren = categoryChild.children; + for (var dsi = 0, dsil = dispScheduleChildren.length; dsi < dsil; dsi++) + { + dispScheduleChild = dispScheduleChildren[dsi]; + if (dispScheduleChild.type == "{http://www.alfresco.org/model/recordsmanagement/1.0}dispositionActionDefinition") + { + // Add Disposition Action description + dispositionActions.push({ + dispositionDescription: dispScheduleChild.properties["rma:dispositionDescription"] + }); + } + } + } + } + + // Add Record Category to the list + recordFolders.sort(sortByName); + recordCategory.recordFolders = recordFolders; + recordCategory.dispositionActions = dispositionActions; + return recordCategory; +} + +/** + * Takes a ScriptNode and builds a Record Category template model from it + * + * @method getRecordFolder + * @param recordFolder {ScriptNode} A ScriptNode of type "rma:recordrecordFolder" + * @param parentPath {string} The file path starting from the top of the fileplan + */ +function getRecordFolder(recordFolder, parentPath) +{ + return { + parentPath: parentPath, + name: recordFolder.name, + identifier: recordFolder.properties["rma:identifier"], + vitalRecordIndicator: recordFolder.properties["vitalRecordIndicator"] + }; +} + +// Start webscript +main(); + diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/fileplanreport.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/fileplanreport.get.json.ftl new file mode 100644 index 0000000000..90a4bf1c06 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/fileplanreport.get.json.ftl @@ -0,0 +1,19 @@ +<#import "fileplanreport.lib.ftl" as reportLib/> +<#macro dateFormat date>${date?string("dd MMM yyyy HH:mm:ss 'GMT'Z '('zzz')'")} +<#escape x as jsonUtils.encodeJSONString(x)> +{ + data: + { + "firstName": <#if person.properties.firstName??>"${person.properties.firstName}"<#else>null, + "lastName": <#if person.properties.lastName??>"${person.properties.lastName}"<#else>null, + <#if (recordSeries??)> + "recordSeries": <@reportLib.recordSeriesJSON recordSeries=recordSeries/>, + <#elseif (recordCategories??)> + "recordCategories": <@reportLib.recordCategoriesJSON recordCategories=recordCategories/>, + <#elseif (recordFolders??)> + "recordFolders": <@reportLib.recordFoldersJSON recordFolders=recordFolders/>, + + "printDate": "<@dateFormat date=date/>" + } +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/fileplanreport.lib.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/fileplanreport.lib.ftl new file mode 100644 index 0000000000..fb5c18680f --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/fileplanreport.lib.ftl @@ -0,0 +1,46 @@ +<#macro recordSeriesJSON recordSeries> +<#escape x as jsonUtils.encodeJSONString(x)> + [<#list recordSeries as recordSerie> + { + "parentPath": "${recordSerie.parentPath}", + "name": "${recordSerie.name}", + "identifier": "${recordSerie.identifier}", + "description": "${recordSerie.description}", + "recordCategories": <@recordCategoriesJSON recordCategories=recordSerie.recordCategories/> + }<#if (recordSerie_has_next)>, + ] + + + +<#macro recordCategoriesJSON recordCategories> +<#escape x as jsonUtils.encodeJSONString(x)> + [<#list recordCategories as recordCategory> + { + "parentPath": "${recordCategory.parentPath}", + "name": "${recordCategory.name}", + "identifier": "${recordCategory.identifier}", + <#if (recordCategory.vitalRecordIndicator??)>"vitalRecordIndicator": ${recordCategory.vitalRecordIndicator?string}, + <#if (recordCategory.dispositionAuthority??)>"dispositionAuthority": "${recordCategory.dispositionAuthority}", + "recordFolders": <@recordFoldersJSON recordFolders=recordCategory.recordFolders/>, + "dispositionActions": [<#list recordCategory.dispositionActions as dispositionAction> + { + "dispositionDescription": "${dispositionAction.dispositionDescription!""}" + }<#if (dispositionAction_has_next)>, + ] + }<#if (recordCategory_has_next)>, + ] + + + +<#macro recordFoldersJSON recordFolders> +<#escape x as jsonUtils.encodeJSONString(x)> + [<#list recordFolders as recordFolder> + { + "parentPath": "${recordFolder.parentPath}", + "name": "${recordFolder.name}", + "identifier": "${recordFolder.identifier}", + <#if (recordFolder.vitalRecordIndicator??)>"vitalRecordIndicator": "${recordFolder.vitalRecordIndicator?string}" + }<#if (recordFolder_has_next)>, + ] + + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/import.post.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/import.post.desc.xml new file mode 100644 index 0000000000..bb2c537379 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/import.post.desc.xml @@ -0,0 +1,17 @@ + + Records Management Import + + The body of the post should be multipart/form-data and contain the following fields.
+
    +
  • destination: array of nodeRefs to export
  • +
  • archive: array of nodeRefs to export
  • +
+ ]]> +
+ /api/rma/admin/import + + user + required + internal +
\ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/import.post.html.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/import.post.html.ftl new file mode 100644 index 0000000000..ff0d596e2d --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/import.post.html.ftl @@ -0,0 +1,14 @@ + + + Upload success + + +<#if (args.successCallback?exists)> + + + + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/import.post.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/import.post.json.ftl new file mode 100644 index 0000000000..1ef9fab7d9 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/import.post.json.ftl @@ -0,0 +1,5 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "success": ${success?string} +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/listofvalues.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/listofvalues.get.desc.xml new file mode 100644 index 0000000000..9b216d0058 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/listofvalues.get.desc.xml @@ -0,0 +1,9 @@ + + List Of Values + Returns lists of items used by the Records Management service + /api/rma/admin/listofvalues + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/listofvalues.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/listofvalues.get.json.ftl new file mode 100644 index 0000000000..a84e447879 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/listofvalues.get.json.ftl @@ -0,0 +1,2 @@ +<#import "listofvalues.lib.ftl" as listsLib/> +<@listsLib.listsJSON lists=lists/> \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/listofvalues.lib.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/listofvalues.lib.ftl new file mode 100644 index 0000000000..ae50014424 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/listofvalues.lib.ftl @@ -0,0 +1,75 @@ +<#macro listsJSON lists> +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": + { + "dispositionActions": + { + "url": "${lists.dispositionActions.url}", + "items": + [ + <#list lists.dispositionActions.items as item> + { + "label": "${item.label}", + "value": "${item.value}" + }<#if item_has_next>, + + ] + }, + "events": + { + "url": "${lists.events.url}", + "items": + [ + <#list lists.events.items as item> + { + "label": "${item.label}", + "value": "${item.value}", + "automatic": ${item.automatic?string} + }<#if item_has_next>, + + ] + }, + "periodTypes": + { + "url": "${lists.periodTypes.url}", + "items": + [ + <#list lists.periodTypes.items as item> + { + "label": "${item.label}", + "value": "${item.value}" + }<#if item_has_next>, + + ] + }, + "periodProperties": + { + "url": "${lists.periodProperties.url}", + "items": + [ + <#list lists.periodProperties.items as item> + { + "label": "${item.label}", + "value": "${item.value}" + }<#if item_has_next>, + + ] + }, + "auditEvents": + { + "url": "${lists.auditEvents.url}", + "items": + [ + <#list lists.auditEvents.items as item> + { + "label": "${item.label}", + "value": "${item.value}" + }<#if item_has_next>, + + ] + } + } +} + + diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/recordmetadataaspects.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/recordmetadataaspects.get.desc.xml new file mode 100644 index 0000000000..14acb48915 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/recordmetadataaspects.get.desc.xml @@ -0,0 +1,12 @@ + + Record Metadata Aspects + + + /api/rma/recordmetadataaspects + + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/recordmetadataaspects.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/recordmetadataaspects.get.json.ftl new file mode 100644 index 0000000000..e83ef198fd --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/recordmetadataaspects.get.json.ftl @@ -0,0 +1,17 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": + { + "recordMetaDataAspects": + [ + <#list aspects as aspect> + { + "id" : "${aspect.id}", + "value" : "${aspect.value}" + } + <#if aspect_has_next>, + + ] + } +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmaction.post.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmaction.post.desc.xml new file mode 100644 index 0000000000..7f4a7f1dd6 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmaction.post.desc.xml @@ -0,0 +1,19 @@ + + Records Management Action Execution + + The body of the post should be in the form
+ {
+    "nodeRef" : nodeRef for target Record,
+    "nodeRefs" : array of nodeRef for target Records (either this or "nodeRef" should be present),
+    "name" : actionName,
+    "params" : {actionParameters}
+ }
+ ]]> +
+ /api/rma/actions/ExecutionQueue + + user + required + internal +
\ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmaction.post.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmaction.post.json.ftl new file mode 100644 index 0000000000..ed22ad29fd --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmaction.post.json.ftl @@ -0,0 +1,14 @@ +{ + "message" : "${message}" +<#if result?exists> + ,"result" : "${result?string}" + +<#if results?exists> + ,"results" : + { + <#list results?keys as prop> + "${prop}" : "${results[prop]}"<#if prop_has_next>, + + } + +} \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmauditlog.delete.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmauditlog.delete.desc.xml new file mode 100644 index 0000000000..8cf91b17a0 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmauditlog.delete.desc.xml @@ -0,0 +1,9 @@ + + Clears Records Management Audit Log + Clears the Records Management audit log + /api/rma/admin/rmauditlog + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmauditlog.delete.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmauditlog.delete.json.ftl new file mode 100644 index 0000000000..9f0631dd99 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmauditlog.delete.json.ftl @@ -0,0 +1,2 @@ +<#import "rmauditlog.lib.ftl" as auditLib/> +<@auditLib.auditStatusJSON auditstatus=auditstatus/> \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmauditlog.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmauditlog.get.desc.xml new file mode 100644 index 0000000000..0f76ff2953 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmauditlog.get.desc.xml @@ -0,0 +1,22 @@ + + Records Management Audit Log + The following parameters can also be passed: +
    +
  • size: Maximum number of log entries to return
  • +
  • user: Only return log entries by the specified user
  • +
  • event: Only return log entries matching this event
  • +
  • from: Only return log entries after the specified date, date should be in yyyy-MM-dd format
  • +
  • to: Only return log entries before the specified date, date should be in yyyy-MM-dd format
  • +
  • export: Set this to 'true' to force the browser to display the Save As dialog
  • +
+ ]]> +
+ /api/rma/admin/rmauditlog + /api/node/{store_type}/{store_id}/{id}/rmauditlog + argument + user + required + internal +
\ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmauditlog.lib.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmauditlog.lib.ftl new file mode 100644 index 0000000000..dd270cebca --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmauditlog.lib.ftl @@ -0,0 +1,12 @@ +<#macro auditStatusJSON auditstatus> +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": + { + "enabled": ${auditstatus.enabled?string}, + "started": "${auditstatus.started}", + "stopped": "${auditstatus.stopped}" + } +} + + diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmauditlog.post.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmauditlog.post.desc.xml new file mode 100644 index 0000000000..d0ddd80ac3 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmauditlog.post.desc.xml @@ -0,0 +1,23 @@ + + Files a Records Management audit log as a record + + A JSON structure is expected as follows:
+ {
+    "destination" : NodeRef of record folder to file the audit log in
+    "size" : Maximum number of log entries to return
+    "user" : Only return log entries by the specified user
+    "event" : Only return log entries matching this event
+    "from" : Only return log entries after the specified date, date should be in yyyy-MM-dd format
+    "to" : Only return log entries before the specified date, date should be in yyyy-MM-dd format
+ }
+ ]]> +
+ /api/rma/admin/rmauditlog + /api/node/{store_type}/{store_id}/{id}/rmauditlog + argument + user + required + internal +
\ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmauditlog.put.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmauditlog.put.desc.xml new file mode 100644 index 0000000000..e13de43b34 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmauditlog.put.desc.xml @@ -0,0 +1,16 @@ + + Start or Stop Records Management Audit Log + The body of the put should be in the form
+ {
+    "enabled" : true|false
+ }
+ ]]> +
+ /api/rma/admin/rmauditlog + argument + user + required + internal +
\ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmauditlog.put.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmauditlog.put.json.ftl new file mode 100644 index 0000000000..9f0631dd99 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmauditlog.put.json.ftl @@ -0,0 +1,2 @@ +<#import "rmauditlog.lib.ftl" as auditLib/> +<@auditLib.auditStatusJSON auditstatus=auditstatus/> \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmauditlogstatus.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmauditlogstatus.get.desc.xml new file mode 100644 index 0000000000..3d32335110 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmauditlogstatus.get.desc.xml @@ -0,0 +1,12 @@ + + Records Management Audit Log Status + + + /api/rma/admin/rmauditlog/status + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmauditlogstatus.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmauditlogstatus.get.json.ftl new file mode 100644 index 0000000000..bbb76957b5 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmauditlogstatus.get.json.ftl @@ -0,0 +1,6 @@ +{ + "data" : + { + "enabled" : ${enabled?string} + } +} \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmconstraints.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmconstraints.get.desc.xml new file mode 100644 index 0000000000..445dc99bf0 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmconstraints.get.desc.xml @@ -0,0 +1,14 @@ + + Get the allowed values for the authenticated user for an rm list constraint. + + listName is the qualified name of the list with the ":" replaced by "_" eg rmc_smList + ]]> + + /api/rma/rmconstraints/{listName} + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmconstraints.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmconstraints.get.json.ftl new file mode 100644 index 0000000000..995b7e3c48 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmconstraints.get.json.ftl @@ -0,0 +1,15 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": { + "constraintName": "${constraintName}", + "allowedValuesForCurrentUser" : [ + <#list allowedValuesForCurrentUser as item> + { + "label": "${item}", + "value": "${item}" + }<#if item_has_next>, + + ] + } +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmpermissions.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmpermissions.get.desc.xml new file mode 100644 index 0000000000..cd90572016 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmpermissions.get.desc.xml @@ -0,0 +1,9 @@ + + Records Management Permissions + Retrieve the Permissions set against a Records Management node. + /api/node/{store_type}/{store_id}/{id}/rmpermissions + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmpermissions.get.js b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmpermissions.get.js new file mode 100644 index 0000000000..439a2654ac --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmpermissions.get.js @@ -0,0 +1,86 @@ +/** + * Entry point for rmpermissions GET data webscript. + * Queries the permissions from an RM node and constructs the data-model for the template. + * + * @method main + */ +function main() +{ + // Get the node from the URL + var pathSegments = url.match.split("/"); + var reference = [ url.templateArgs.store_type, url.templateArgs.store_id ].concat(url.templateArgs.id.split("/")); + var node = search.findNode(pathSegments[2], reference); + + // 404 if the node is not found + if (node == null) + { + status.setCode(status.STATUS_NOT_FOUND, "The node could not be found"); + return; + } + + // retrieve permissions applied to this node + var permissions = node.getFullPermissions(); + + // split tokens - results are in the format: + // [ALLOWED|DENIED];[USERNAME|GROUPNAME];PERMISSION;[INHERITED|DIRECT] + var result = []; + for (var i=0; i +{ + "data": + { + "permissions": + [ + <#list permissions as perm> + { + "id": "${perm.id}", + "authority": + { + "id": "${perm.authority.id}", + "label": "${perm.authority.label}" + }, + "inherited": ${perm.inherited?string} + }<#if perm_has_next>, + + ], + "inherited": ${inherited?string} + } +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmpermissions.post.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmpermissions.post.desc.xml new file mode 100644 index 0000000000..e96bac280c --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmpermissions.post.desc.xml @@ -0,0 +1,36 @@ + + Apply Records Management Permissions to a node + +
+ The body of the post json should be of the form: +
+   {
+      "permissions":
+      [
+         {
+            "id": "Filing",
+            "authority": "GROUP_Administrator"
+         },
+         {
+            "id": "ReadRecords",
+            "authority": "userxyz"
+            "remove": true
+         },
+         ...
+      ]
+   }
+   
+ Existing permissions will be updated by the supplied permission set, + where 'id' and 'authority' are mandatory values.
+ If the optional 'remove' flag is set then the permission will be removed. + Note that it is only valid to set the following RM related permissions: + 'Filing', 'ReadRecords' and 'FileRecords'. + ]]> +
+ /api/node/{store_type}/{store_id}/{id}/rmpermissions + argument + user + required + internal +
\ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmpermissions.post.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmpermissions.post.json.ftl new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmpermissions.post.json.ftl @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmpermissions.post.json.js b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmpermissions.post.json.js new file mode 100644 index 0000000000..6aa731567e --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/rmpermissions.post.json.js @@ -0,0 +1,52 @@ +/** + * Entry point for rmpermissions POST data webscript. + * Applies supplied RM permissions to an RM node. + * + * @method main + */ +function main() +{ + // Get the node from the URL + var pathSegments = url.match.split("/"); + var reference = [ url.templateArgs.store_type, url.templateArgs.store_id ].concat(url.templateArgs.id.split("/")); + var node = search.findNode(pathSegments[2], reference); + + // 404 if the node is not found + if (node == null) + { + status.setCode(status.STATUS_NOT_FOUND, "The node could not be found"); + return; + } + + if (json.has("permissions") == false) + { + status.setCode(status.STATUS_BAD_REQUEST, "Permissions value missing from request."); + } + + var permissions = json.getJSONArray("permissions"); + for (var i=0; i + Records Management Transfer + Streams an Alfresco Content Pacakge (ACP) file containing the contents of a transfer + /api/node/{store_type}/{store_id}/{id}/transfers/{transfer_id} + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/transferreport.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/transferreport.get.desc.xml new file mode 100644 index 0000000000..727c0c7a6a --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/transferreport.get.desc.xml @@ -0,0 +1,9 @@ + + Records Management Transfer Report + Returns a transfer report to the caller in JSON format + /api/node/{store_type}/{store_id}/{id}/transfers/{transfer_id}/report + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/transferreport.post.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/transferreport.post.desc.xml new file mode 100644 index 0000000000..37c74d9a73 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/transferreport.post.desc.xml @@ -0,0 +1,16 @@ + + Files a Records Management Transfer Report + A JSON structure is expected as follows:
+ {
+    "destination" : NodeRef of record folder to file the transfer report in
+ }
+ ]]> +
+ /api/node/{store_type}/{store_id}/{id}/transfers/{transfer_id}/report + argument + user + required + internal +
\ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/userrightsreport.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/userrightsreport.get.desc.xml new file mode 100644 index 0000000000..d0151a4915 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/userrightsreport.get.desc.xml @@ -0,0 +1,9 @@ + + Records Management User Rights Report + Returns a user rights report showing users, roles and groups to the caller in JSON format + /api/rma/admin/userrightsreport + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/userrightsreport.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/userrightsreport.get.json.ftl new file mode 100644 index 0000000000..81e88674a3 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/rma/userrightsreport.get.json.ftl @@ -0,0 +1,46 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": + { + "users": + { + <#list report.users?keys as user> + "${user}": + { + "userName": "${report.users[user].userName!""}", + "firstName": "${report.users[user].firstName!""}", + "lastName": "${report.users[user].lastName!""}", + "roles": [<#list report.users[user].roles as role>"${role}"<#if role_has_next>,], + "groups": [<#list report.users[user].groups as group>"${group}"<#if group_has_next>,] + } + <#if user_has_next>, + + }, + "roles": + { + <#list report.roles?keys as role> + "${role}": + { + "name": "${report.roles[role].name!""}", + "label": "${report.roles[role].displayLabel!""}", + "users": [<#list report.roles[role].users as user>"${user}"<#if user_has_next>,], + "capabilities": [<#list report.roles[role].capabilities as capability>"${capability}"<#if capability_has_next>,] + } + <#if role_has_next>, + + }, + "groups": + { + <#list report.groups?keys as group> + "${group}": + { + "name": "${report.groups[group].name!""}", + "label": "${report.groups[group].displayLabel!""}", + "users": [<#list report.groups[group].users as user>"${user}"<#if user_has_next>,] + } + <#if group_has_next>, + + } + } +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/doclist.lib.js b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/doclist.lib.js new file mode 100644 index 0000000000..947666762f --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/doclist.lib.js @@ -0,0 +1,264 @@ +const REQUEST_MAX = 1000; + +/** + * Main entry point: Create collection of documents and folders in the given space + * + * @method doclist_main + */ +function doclist_main() +{ + // Use helper function to get the arguments + var parsedArgs = ParseArgs.getParsedArgs(); + if (parsedArgs === null) + { + return; + } + + var filter = args.filter, + items = []; + + // Try to find a filter query based on the passed-in arguments + var allNodes = [], + totalRecords = 0, + requestTotalCountMax = 0, + paged = false, + favourites = Common.getFavourites(), + filterParams = Filters.getFilterParams(filter, parsedArgs, + { + favourites: favourites + }), + query = filterParams.query; + + if ((filter || "path") == "path") + { + // TODO also add DB filter by "node" (in addition to "path") + var parentNode = parsedArgs.pathNode; + if (parentNode !== null) + { + var skip = -1, + max = -1; + + if (args.size != null) + { + max = args.size; + + if (args.pos > 0) + { + skip = (args.pos - 1) * max; + } + } + + var sortField = (args.sortField == null ? "cm:name" : args.sortField), + sortAsc = (((args.sortAsc == null) || (args.sortAsc == "true")) ? true : false); + + // Get paged set + requestTotalCountMax = skip + REQUEST_MAX; + var pagedResult = parentNode.childFileFolders(true, true, filterParams.ignoreTypes, skip, max, requestTotalCountMax, sortField, sortAsc, "TODO"); + + allNodes = pagedResult.page; + totalRecords = pagedResult.totalResultCountUpper; + paged = true; + } + } + else + { + // Query the nodes - passing in sort and result limit parameters + if (query !== "") + { + allNodes = search.query( + { + query: query, + language: filterParams.language, + page: + { + maxItems: (filterParams.limitResults ? parseInt(filterParams.limitResults, 10) : 0) + }, + sort: filterParams.sort, + templates: filterParams.templates, + namespace: (filterParams.namespace ? filterParams.namespace : null) + }); + + totalRecords = allNodes.length; + } + } + + // Ensure folders and folderlinks appear at the top of the list + var folderNodes = [], + documentNodes = []; + + for each (node in allNodes) + { + try + { + if (node.isContainer || node.isLinkToContainer) + { + folderNodes.push(node); + } + else + { + documentNodes.push(node); + } + } + catch (e) + { + // Possibly an old indexed node - ignore it + } + } + + // Node type counts + var folderNodesCount = folderNodes.length, + documentNodesCount = documentNodes.length, + nodes; + + if (parsedArgs.type === "documents") + { + nodes = documentNodes; + totalRecords -= folderNodesCount; + } + else + { + // TODO: Sorting with folders at end -- swap order of concat() + nodes = folderNodes.concat(documentNodes); + } + + // Pagination + var pageSize = args.size || nodes.length, + pagePos = args.pos || "1", + startIndex = (pagePos - 1) * pageSize; + + if (!paged) + { + // Trim the nodes array down to the page size + nodes = nodes.slice(startIndex, pagePos * pageSize); + } + + // Common or variable parent container? + var parent = null; + + if (!filterParams.variablePath) + { + // Parent node permissions (and Site role if applicable) + parent = Evaluator.run(parsedArgs.pathNode, true); + } + + var isThumbnailNameRegistered = thumbnailService.isThumbnailNameRegistered(THUMBNAIL_NAME), + thumbnail = null, + locationNode, + item; + + // Loop through and evaluate each node in this result set + for each (node in nodes) + { + // Get evaluated properties. + item = Evaluator.run(node); + if (item !== null) + { + item.isFavourite = (favourites[item.node.nodeRef] === true); + item.likes = Common.getLikes(node); + + // Does this collection of nodes have potentially differering paths? + if (filterParams.variablePath || item.isLink) + { + locationNode = item.isLink ? item.linkedNode : item.node; + location = Common.getLocation(locationNode, parsedArgs.libraryRoot); + // Parent node + if (node.parent != null && + (node.parent.hasPermission("Read") || node.parent.hasPermission("ReadRecords"))) + { + item.parent = Evaluator.run(node.parent, true); + } + } + else + { + location = + { + site: parsedArgs.location.site, + siteTitle: parsedArgs.location.siteTitle, + sitePreset: parsedArgs.location.sitePreset, + container: parsedArgs.location.container, + containerType: parsedArgs.location.containerType, + path: parsedArgs.location.path, + file: node.name + }; + } + + // Resolved location + item.location = location; + + // Check: thumbnail type is registered && node is a cm:content subtype && valid inputStream for content property + if (isThumbnailNameRegistered && item.node.isSubType("cm:content") && item.node.properties.content.inputStream != null) + { + // Make sure we have a thumbnail. + thumbnail = item.node.getThumbnail(THUMBNAIL_NAME); + if (thumbnail === null) + { + // No thumbnail, so queue creation + item.node.createThumbnail(THUMBNAIL_NAME, true); + } + } + + items.push(item); + } + else + { + --totalRecords; + } + } + + // Array Remove - By John Resig (MIT Licensed) + var fnArrayRemove = function fnArrayRemove(array, from, to) + { + var rest = array.slice((to || from) + 1 || array.length); + array.length = from < 0 ? array.length + from : from; + return array.push.apply(array, rest); + }; + + /** + * De-duplicate orignals for any existing working copies. + * This can't be done in evaluator.lib.js as it has no knowledge of the current filter or UI operation. + * Note: This may result in pages containing less than the configured amount of items (50 by default). + */ + for each (item in items) + { + if (item.workingCopy.isWorkingCopy) + { + var workingCopySource = String(item.workingCopy.sourceNodeRef); + for (var i = 0, ii = items.length; i < ii; i++) + { + if (String(items[i].node.nodeRef) == workingCopySource) + { + fnArrayRemove(items, i); + --totalRecords; + break; + } + } + } + } + + var paging = + { + totalRecords: totalRecords, + startIndex: startIndex + }; + + if (paged && (totalRecords == requestTotalCountMax)) + { + paging.totalRecordsUpper = requestTotalCountMax; + } + + return ( + { + luceneQuery: query, + paging: paging, + container: parsedArgs.rootNode, + parent: parent, + onlineEditing: utils.moduleInstalled("org.alfresco.module.vti"), + itemCount: + { + folders: folderNodesCount, + documents: documentNodesCount + }, + items: items, + customJSON: slingshotDocLib.getJSON() + }); +} diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/node.get.js b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/node.get.js new file mode 100644 index 0000000000..145314fb8c --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/node.get.js @@ -0,0 +1,77 @@ + + + +/** + * Main entry point: Return single document or folder given it's nodeRef + * + * @method getDoclist + */ +function getDoclist() +{ + // Use helper function to get the arguments + var parsedArgs = ParseArgs.getParsedArgs(); + if (parsedArgs === null) + { + return; + } + + parsedArgs.pathNode = ParseArgs.resolveNode(parsedArgs.nodeRef); + parsedArgs.location = Common.getLocation(parsedArgs.pathNode, parsedArgs.libraryRoot); + + var favourites = Common.getFavourites(), + node = parsedArgs.pathNode; + + var isThumbnailNameRegistered = thumbnailService.isThumbnailNameRegistered(THUMBNAIL_NAME), + thumbnail = null, + item = Evaluator.run(node); + + item.isFavourite = (favourites[node.nodeRef] === true); + item.likes = Common.getLikes(node); + item.location = + { + site: parsedArgs.location.site, + siteTitle: parsedArgs.location.siteTitle, + container: parsedArgs.location.container, + containerType: parsedArgs.location.containerType, + path: parsedArgs.location.path, + file: node.name + }; + + item.parent = {}; + if (node.parent != null && (node.parent.hasPermission("Read") || node.parent.hasPermission("ReadRecords"))) + { + item.parent = Evaluator.run(node.parent, true); + } + + // Special case for container and libraryRoot nodes + if ((parsedArgs.location.containerNode && String(parsedArgs.location.containerNode.nodeRef) == String(node.nodeRef)) || + (parsedArgs.libraryRoot && String(parsedArgs.libraryRoot.nodeRef) == String(node.nodeRef))) + { + item.location.file = ""; + } + + // Check: thumbnail type is registered && node is a cm:content subtype && valid inputStream for content property + if (isThumbnailNameRegistered && item.node.isSubType("cm:content") && item.node.properties.content.inputStream != null) + { + // Make sure we have a thumbnail. + thumbnail = item.node.getThumbnail(THUMBNAIL_NAME); + if (thumbnail === null) + { + // No thumbnail, so queue creation + item.node.createThumbnail(THUMBNAIL_NAME, true); + } + } + + return ( + { + container: parsedArgs.rootNode, + onlineEditing: utils.moduleInstalled("org.alfresco.module.vti"), + item: item, + customJSON: slingshotDocLib.getJSON() + }); +} + +/** + * Document List Component: doclist + */ +model.doclist = getDoclist(); \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/rm-doclist.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/rm-doclist.get.desc.xml new file mode 100644 index 0000000000..8b8441f74d --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/rm-doclist.get.desc.xml @@ -0,0 +1,12 @@ + + doclist-v2 + Document List v2 Component - records management doclist data webscript + /slingshot/doclib2/rm/doclist/{type}/site/{site}/{container}/{path} + /slingshot/doclib2/rm/doclist/{type}/site/{site}/{container} + /slingshot/doclib2/rm/doclist/{type}/node/{store_type}/{store_id}/{id}/{path} + /slingshot/doclib2/rm/doclist/{type}/node/{store_type}/{store_id}/{id} + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/rm-doclist.get.js b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/rm-doclist.get.js new file mode 100644 index 0000000000..ac91752be0 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/rm-doclist.get.js @@ -0,0 +1,10 @@ + + + + + + +/** + * Document List Component: doclist + */ +model.doclist = doclist_main(); diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/rm-doclist.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/rm-doclist.get.json.ftl new file mode 100644 index 0000000000..df84dfc21e --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/rm-doclist.get.json.ftl @@ -0,0 +1 @@ +<#include "doclist.get.json.ftl"> \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/rm-filters.lib.js b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/rm-filters.lib.js new file mode 100644 index 0000000000..25a0234d47 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/rm-filters.lib.js @@ -0,0 +1,197 @@ +/** +* Query templates for custom search +*/ +Filters.QUERY_TEMPLATES = +[ + { field: "keywords", template: "%(cm:name cm:title cm:description TEXT)" }, + { field: "name", template: "%(cm:name)" }, + { field: "title", template: "%(cm:title)" }, + { field: "description", template: "%(cm:description)" }, + { field: "creator", template: "%(cm:creator)" }, + { field: "created", template: "%(cm:created)" }, + { field: "modifier", template: "%(cm:modifier)" }, + { field: "modified", template: "%(cm:modified)" }, + { field: "author", template: "%(cm:author)" }, + { field: "markings", template: "%(rmc:supplementalMarkingList)" }, + { field: "dispositionEvents", template: "%(rma:recordSearchDispositionEvents)" }, + { field: "dispositionActionName", template: "%(rma:recordSearchDispositionActionName)" }, + { field: "dispositionActionAsOf", template: "%(rma:recordSearchDispositionActionAsOf)" }, + { field: "dispositionEventsEligible", template: "%(rma:recordSearchDispositionEventsEligible)" }, + { field: "dispositionPeriod", template: "%(rma:recordSearchDispositionPeriod)" }, + { field: "hasDispositionSchedule", template: "%(rma:recordSearchHasDispositionSchedule)" }, + { field: "dispositionInstructions", template: "%(rma:recordSearchDispositionInstructions)" }, + { field: "dispositionAuthority", template: "%(rma:recordSearchDispositionAuthority)" }, + { field: "holdReason", template: "%(rma:recordSearchHoldReason)" }, + { field: "vitalRecordReviewPeriod", template: "%(rma:recordSearchVitalRecordReviewPeriod)" } +]; + +Filters.IGNORED_TYPES = +[ + /* Defaults */ + "cm:systemfolder", + "fm:forums", + "fm:forum", + "fm:topic", + "fm:post", + /* Records Management */ + "rma:dispositionSchedule", + "rma:dispositionActionDefinition", + "rma:dispositionAction", + "rma:hold", + "rma:transfer" +]; + +/** + * Create filter parameters based on input parameters + * + * @method getFilterParams + * @param filter {string} Required filter + * @param parsedArgs {object} Parsed arguments object literal + * @param optional {object} Optional arguments depending on filter type + * @return {object} Object literal containing parameters to be used in Lucene search + */ +Filters.getFilterParams = function RecordsManagementFilter_getFilterParams(filter, parsedArgs, optional) +{ + var filterParams = + { + query: "+PATH:\"" + parsedArgs.pathNode.qnamePath + "/*\"", + limitResults: null, + sort: [ + { + column: "@cm:name", + ascending: true + }], + language: "lucene", + templates: null, + variablePath: true, + ignoreTypes: Filters.IGNORED_TYPES + }; + + optional = optional || {}; + + // Sorting parameters specified? + var sortAscending = args.sortAsc, + sortField = args.sortField; + + if (sortAscending == "false") + { + filterParams.sort[0].ascending = false; + } + if (sortField !== null) + { + filterParams.sort[0].column = (sortField.indexOf(":") != -1 ? "@" : "") + sortField; + } + + // Max returned results specified? + var argMax = args.max; + if ((argMax !== null) && !isNaN(argMax)) + { + filterParams.limitResults = argMax; + } + + // Create query based on passed-in arguments + var filterData = args.filterData, + filterQuery = ""; + + // Common types and aspects to filter from the UI + var filterQueryDefaults = ' -TYPE:"' + Filters.IGNORED_TYPES.join('" -TYPE:"') + '"'; + + // Create query based on passed-in arguments + switch (String(filter)) + { + case "all": + filterQuery = "+PATH:\"" + parsedArgs.rootNode.qnamePath + "//*\""; + filterQuery += " -TYPE:\"{http://www.alfresco.org/model/content/1.0}folder\""; + filterParams.query = filterQuery + filterQueryDefaults; + break; + + case "node": + parsedArgs.pathNode = parsedArgs.rootNode.parent; + filterParams.variablePath = false; + filterParams.query = "+ID:\"" + parsedArgs.rootNode.nodeRef + "\""; + break; + + case "savedsearch": + var searchNode = parsedArgs.location.siteNode.getContainer("Saved Searches"); + if (searchNode != null) + { + var ssNode = searchNode.childByNamePath(String(filterData)); + + if (ssNode != null) + { + var ssJson = eval('try{(' + ssNode.content + ')}catch(e){}'); + filterQuery = ssJson.query; + // Wrap the query so that only valid items within the filePlan are returned + filterParams.query = 'PATH:"' + parsedArgs.rootNode.qnamePath + '//*" AND (' + filterQuery + ')'; + filterParams.templates = Filters.QUERY_TEMPLATES; + filterParams.language = "fts-alfresco"; + filterParams.namespace = "http://www.alfresco.org/model/recordsmanagement/1.0"; + // gather up the sort by fields + // they are encoded as "property/dir" i.e. "cm:name/asc" + if (ssJson.sort && ssJson.sort.length !== 0) + { + var sortPairs = ssJson.sort.split(","); + var sort = []; + for (var i=0, j; i a.properties["rma:dispositionActionCompletedAt"] ? 1 : -1); + }; + + if (history != null) + { + history.sort(fnSortByCompletionDateReverse); + previous = history[0]; + } + + return previous; + }, + + /** + * Record and Record Folder common evaluators + */ + recordAndRecordFolder: function Evaluator_recordAndRecordFolder(asset, permissions, status) + { + var actionName = asset.properties["rma:recordSearchDispositionActionName"], + actionAsOf = asset.properties["rma:recordSearchDispositionActionAsOf"], + hasNextAction = asset.childAssocs["rma:nextDispositionAction"] != null, + recentHistory = Evaluator.getPreviousDispositionAction(asset), + previousAction = null, + now = new Date(); + + /* Next Disposition Action */ + // Next action could become eligible based on asOf date + if (actionAsOf != null) + { + if (hasNextAction) + { + permissions["disposition-as-of"] = true; + } + + // Check if action asOf date has passed + if (actionAsOf < now) + { + permissions[actionName] = true; + } + } + // Next action could also become eligible based on event completion + if (asset.properties["rma:recordSearchDispositionEventsEligible"] == true) + { + permissions[actionName] = true; + } + + /* Previous Disposition Action */ + if (recentHistory != null) + { + previousAction = recentHistory.properties["rma:dispositionAction"]; + } + + /* Cut Off status */ + if (asset.hasAspect("rma:cutOff")) + { + status["cutoff"] = true; + if (asset.hasAspect("rma:dispositionLifecycle")) + { + if (previousAction == "cutoff") + { + permissions["undo-cutoff"] = true; + delete permissions["cutoff"]; + } + } + } + + /* Transfer or Accession Pending Completion */ + // Don't show transfer or accession if either is pending completion + var assocs = asset.parentAssocs["rma:transferred"]; + if (actionName == "transfer" && assocs != null && assocs.length > 0) + { + delete permissions["transfer"]; + delete permissions["undo-cutoff"]; + delete permissions["disposition-as-of"]; + status["transfer " + assocs[0].name] = true; + } + assocs = asset.parentAssocs["rma:ascended"]; + if (actionName == "accession" && assocs != null && assocs.length > 0) + { + delete permissions["accession"]; + delete permissions["undo-cutoff"]; + delete permissions["disposition-as-of"]; + status["accession " + assocs[0].name] = true; + } + + /* Transferred status */ + if (asset.hasAspect("rma:transferred")) + { + var transferLocation = ""; + if (previousAction == "transfer") + { + var actionId = recentHistory.properties["rma:dispositionActionId"], + actionNode = search.findNode("workspace://SpacesStore/" + actionId); + + if (actionNode != null && actionNode.properties["rma:dispositionLocation"]) + { + transferLocation = " " + actionNode.properties["rma:dispositionLocation"]; + } + } + status["transferred" + transferLocation] = true; + } + + /* Accessioned status */ + if (asset.hasAspect("rma:ascended")) + { + status["accessioned NARA"] = true; + } + + /* Review As Of Date */ + if (asset.hasAspect("rma:vitalRecord")) + { + if (asset.properties["rma:reviewAsOf"] != null) + { + permissions["review-as-of"] = true; + } + } + + /* Frozen/Unfrozen */ + if (asset.hasAspect("rma:frozen")) + { + status["frozen"] = true; + if (permissions["Unfreeze"]) + { + permissions["unfreeze"] = true; + } + } + else + { + if (permissions["ExtendRetentionPeriodOrFreeze"]) + { + permissions["freeze"] = true; + } + } + }, + + /** + * Record Type evaluator + */ + recordType: function Evaluator_recordType(asset) + { + /* Supported Record Types */ + var recordTypes = + [ + "digitalPhotographRecord", + "pdfRecord", + "scannedRecord", + "webRecord" + ], + currentRecordType = null; + + for (var i = 0; i < recordTypes.length; i++) + { + if (asset.hasAspect("dod:" + recordTypes[i])) + { + currentRecordType = recordTypes[i]; + break; + } + } + + return currentRecordType; + }, + + /** + * Asset Evaluator - main entrypoint + */ + run: function Evaluator_run(asset, capabilitySet) + { + var assetType = Evaluator.getAssetType(asset), + rmNode, + recordType = null, + capabilities = {}, + actions = {}, + actionSet = "empty", + permissions = {}, + status = {}, + suppressRoles = false; + + var now = new Date(); + + try + { + rmNode = rmService.getRecordsManagementNode(asset) + } + catch (e) + { + // Not a Records Management Node + return null; + } + + /** + * Capabilities and Actions + */ + var caps, cap, act; + if (capabilitySet == "all") + { + caps = rmNode.capabilities; + } + else + { + caps = rmNode.capabilitiesSet(capabilitySet); + } + + for each (cap in caps) + { + capabilities[cap.name] = true; + for each (act in cap.actions) + { + actions[act] = true; + } + } + + /** + * COMMON FOR ALL TYPES + */ + + /** + * Basic permissions - start from entire capabiltiies list + * TODO: Filter-out the ones not relevant to DocLib UI. + */ + permissions = capabilities; + + /** + * Multiple parent assocs + */ + var parents = asset.parentAssocs["contains"]; + if (parents !== null && parents.length > 1) + { + status["multi-parent " + parents.length] = true; + } + + /** + * E-mail type + */ + if (asset.mimetype == "message/rfc822") + { + permissions["split-email"] = true; + } + + switch (assetType) + { + /** + * SPECIFIC TO: FILE PLAN + */ + case "fileplan": + permissions["new-series"] = capabilities["Create"]; + break; + + + /** + * SPECIFIC TO: RECORD SERIES + */ + case "record-series": + actionSet = "recordSeries"; + permissions["new-category"] = capabilities["Create"]; + break; + + + /** + * SPECIFIC TO: RECORD CATEGORY + */ + case "record-category": + actionSet = "recordCategory"; + permissions["new-folder"] = capabilities["Create"]; + break; + + + /** + * SPECIFIC TO: RECORD FOLDER + */ + case "record-folder": + actionSet = "recordFolder"; + + /* Record and Record Folder common evaluator */ + Evaluator.recordAndRecordFolder(asset, permissions, status); + + /* Update Cut Off status to folder-specific status */ + if (status["cutoff"] == true) + { + delete status["cutoff"]; + status["cutoff-folder"] = true; + } + + /* File new Records */ + permissions["file"] = capabilities["Create"]; + + /* Open/Closed */ + if (asset.properties["rma:isClosed"]) + { + // Cutoff implies closed, so no need to duplicate + if (!status["cutoff-folder"]) + { + status["closed"] = true; + } + if (capabilities["ReOpenFolders"]) + { + permissions["open-folder"] = true; + } + } + else + { + status["open"] = true; + if (capabilities["CloseFolders"]) + { + permissions["close-folder"] = true; + } + } + break; + + + /** + * SPECIFIC TO: RECORD + */ + case "record": + actionSet = "record"; + + /* Record and Record Folder common evaluator */ + Evaluator.recordAndRecordFolder(asset, permissions, status); + + /* Electronic/Non-electronic documents */ + if (asset.typeShort == "rma:nonElectronicDocument") + { + assetType = "record-nonelec"; + } + else + { + permissions["download"] = true; + } + + /* Record Type evaluator */ + recordType = Evaluator.recordType(asset); + if (recordType != null) + { + status[recordType] = true; + } + + /* Undeclare Record */ + if (asset.hasAspect("rma:cutOff") == false) + { + permissions["undeclare"] = true; + } + break; + + + /** + * SPECIFIC TO: GHOSTED RECORD FOLDER (Metadata Stub Folder) + */ + case "metadata-stub-folder": + actionSet = "metadataStubFolder"; + + /* Destroyed status */ + status["destroyed"] = true; + break; + + + /** + * SPECIFIC TO: GHOSTED RECORD (Metadata Stub) + */ + case "metadata-stub": + actionSet = "metadataStub"; + + /* Destroyed status */ + status["destroyed"] = true; + + /* Record Type evaluator */ + recordType = Evaluator.recordType(asset); + if (recordType != null) + { + status[recordType] = true; + } + break; + + + /** + * SPECIFIC TO: UNDECLARED RECORD + */ + case "undeclared-record": + actionSet = "undeclaredRecord"; + + /* Electronic/Non-electronic documents */ + if (asset.typeShort == "rma:nonElectronicDocument") + { + assetType = "undeclared-record-nonelec"; + } + else + { + permissions["download"] = true; + + /* Record Type evaluator */ + recordType = Evaluator.recordType(asset); + if (recordType != null) + { + status[recordType] = true; + } + else + { + permissions["set-record-type"] = true; + } + } + break; + + + /** + * SPECIFIC TO: TRANSFER CONTAINERS + */ + case "transfer-container": + actionSet = "transferContainer"; + suppressRoles = true; + break; + + + /** + * SPECIFIC TO: HOLD CONTAINERS + */ + case "hold-container": + actionSet = "holdContainer"; + permissions["Unfreeze"] = true; + permissions["ViewUpdateReasonsForFreeze"] = true; + suppressRoles = true; + break; + + + /** + * SPECIFIC TO: LEGACY TYPES + */ + default: + actionSet = assetType; + break; + } + + return ( + { + assetType: assetType, + actionSet: actionSet, + permissions: permissions, + createdBy: Common.getPerson(asset.properties["cm:creator"]), + modifiedBy: Common.getPerson(asset.properties["cm:modifier"]), + status: status, + metadata: Evaluator.getMetadata(asset, assetType), + suppressRoles: suppressRoles + }); + } +}; diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-filters.lib.js b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-filters.lib.js new file mode 100644 index 0000000000..9949dd5dbc --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-filters.lib.js @@ -0,0 +1,183 @@ +var Filters = +{ + /** + * Type map to filter required types + * NOTE: "documents" filter also returns folders to show UI hint about hidden folders. + */ + TYPE_MAP: + { + "documents": '+(TYPE:"{http://www.alfresco.org/model/content/1.0}content" OR TYPE:"{http://www.alfresco.org/model/application/1.0}filelink" OR TYPE:"{http://www.alfresco.org/model/content/1.0}folder")', + "folders": '+(TYPE:"{http://www.alfresco.org/model/content/1.0}folder" OR TYPE:"{http://www.alfresco.org/model/application/1.0}folderlink")', + "images": "-TYPE:\"{http://www.alfresco.org/model/content/1.0}thumbnail\" +@cm\\:content.mimetype:image/*" + }, + + /** + * Query templates for custom search + */ + QUERY_TEMPLATES: + [ + {field: "keywords", template: "%(cm:name cm:title cm:description TEXT)"}, + {field: "name", template: "%(cm:name)"}, + {field: "title", template: "%(cm:title)"}, + {field: "description", template: "%(cm:description)"}, + {field: "creator", template: "%(cm:creator)"}, + {field: "created", template: "%(cm:created)"}, + {field: "modifier", template: "%(cm:modifier)"}, + {field: "modified", template: "%(cm:modified)"}, + {field: "author", template: "%(cm:author)"}, + {field: "markings", template: "%(rmc:supplementalMarkingList)"}, + {field: "dispositionEvents", template: "%(rma:recordSearchDispositionEvents)"}, + {field: "dispositionActionName", template: "%(rma:recordSearchDispositionActionName)"}, + {field: "dispositionActionAsOf", template: "%(rma:recordSearchDispositionActionAsOf)"}, + {field: "dispositionEventsEligible", template: "%(rma:recordSearchDispositionEventsEligible)"}, + {field: "dispositionPeriod", template: "%(rma:recordSearchDispositionPeriod)"}, + {field: "hasDispositionSchedule", template: "%(rma:recordSearchHasDispositionSchedule)"}, + {field: "dispositionInstructions", template: "%(rma:recordSearchDispositionInstructions)"}, + {field: "dispositionAuthority", template: "%(rma:recordSearchDispositionAuthority)"}, + {field: "holdReason", template: "%(rma:recordSearchHoldReason)"}, + {field: "vitalRecordReviewPeriod", template: "%(rma:recordSearchVitalRecordReviewPeriod)"} + ], + + /** + * Create filter parameters based on input parameters + * + * @method getFilterParams + * @param filter {string} Required filter + * @param parsedArgs {object} Parsed arguments object literal + * @param optional {object} Optional arguments depending on filter type + * @return {object} Object literal containing parameters to be used in Lucene search + */ + getFilterParams: function Filter_getFilterParams(filter, parsedArgs, optional) + { + var filterParams = + { + query: "+PATH:\"" + parsedArgs.pathNode.qnamePath + "/*\"", + limitResults: null, + sort: [ + { + column: "@{http://www.alfresco.org/model/content/1.0}name", + ascending: true + }], + language: "lucene", + templates: null, + variablePath: true + }; + + // Max returned results specified? + var argMax = args.max; + if ((argMax !== null) && !isNaN(argMax)) + { + filterParams.limitResults = argMax; + } + + // Create query based on passed-in arguments + var filterData = args.filterData, + filterQuery = ""; + + // Common types and aspects to filter from the UI + var filterQueryDefaults = " -TYPE:\"{http://www.alfresco.org/model/content/1.0}thumbnail\"" + + " -TYPE:\"{http://www.alfresco.org/model/content/1.0}systemfolder\"" + + " -TYPE:\"{http://www.alfresco.org/model/recordsmanagement/1.0}dispositionSchedule\"" + + " -TYPE:\"{http://www.alfresco.org/model/recordsmanagement/1.0}dispositionActionDefinition\"" + + " -TYPE:\"{http://www.alfresco.org/model/recordsmanagement/1.0}dispositionAction\"" + + " -TYPE:\"{http://www.alfresco.org/model/recordsmanagement/1.0}hold\"" + + " -TYPE:\"{http://www.alfresco.org/model/recordsmanagement/1.0}transfer\""; + + // Create query based on passed-in arguments + switch (String(filter)) + { + case "all": + filterQuery = "+PATH:\"" + parsedArgs.rootNode.qnamePath + "//*\""; + filterQuery += " -TYPE:\"{http://www.alfresco.org/model/content/1.0}folder\""; + filterParams.query = filterQuery + filterQueryDefaults; + break; + + case "node": + parsedArgs.pathNode = parsedArgs.rootNode.parent; + filterParams.variablePath = false; + filterParams.query = "+ID:\"" + parsedArgs.rootNode.nodeRef + "\""; + break; + + case "savedsearch": + var searchNode = parsedArgs.location.siteNode.getContainer("Saved Searches"); + if (searchNode != null) + { + var ssNode = searchNode.childByNamePath(String(filterData)); + + if (ssNode != null) + { + var ssJson = eval('try{(' + ssNode.content + ')}catch(e){}'); + filterQuery = ssJson.query; + // Wrap the query so that only valid items within the filePlan are returned + filterParams.query = 'PATH:"' + parsedArgs.rootNode.qnamePath + '//*" AND (' + filterQuery + ')'; + filterParams.templates = Filters.QUERY_TEMPLATES; + filterParams.language = "fts-alfresco"; + filterParams.namespace = "http://www.alfresco.org/model/recordsmanagement/1.0"; + // gather up the sort by fields + // they are encoded as "property/dir" i.e. "cm:name/asc" + if (ssJson.sort && ssJson.sort.length !== 0) + { + var sortPairs = ssJson.sort.split(","); + var sort = []; + for (var i=0, j; i + node + Document List Component - rm node data webscript + /slingshot/doclib/rm/node/{store_type}/{store_id}/{id} + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-node.get.js b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-node.get.js new file mode 100644 index 0000000000..9dd3b3b48c --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-node.get.js @@ -0,0 +1,6 @@ + + + + + +model.doclist = getDoclist("all"); diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-node.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-node.get.json.ftl new file mode 100644 index 0000000000..81e520bab6 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-node.get.json.ftl @@ -0,0 +1,34 @@ +<#import "item.lib.ftl" as itemLib /> +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "totalRecords": ${doclist.paging.totalRecords?c}, + "startIndex": ${doclist.paging.startIndex?c}, + "metadata": + { + <#if doclist.filePlan??>"filePlan": "${doclist.filePlan.nodeRef}", + "parent": + { + <#if doclist.parent??> + "nodeRef": "${doclist.parent.node.nodeRef}", + "type": "${doclist.parent.type}", + "permissions": + { + "userAccess": + { + <#list doclist.parent.userAccess?keys as perm> + <#if doclist.parent.userAccess[perm]?is_boolean> + "${perm?string}": ${doclist.parent.userAccess[perm]?string}<#if perm_has_next>, + + + } + } + + } + }, + "item": + { + <@itemLib.itemJSON item=doclist.items[0] />, + "dod5015": <#noescape>${doclist.items[0].dod5015} + } +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-savedsearches.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-savedsearches.get.desc.xml new file mode 100644 index 0000000000..3b23c5431b --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-savedsearches.get.desc.xml @@ -0,0 +1,9 @@ + + doclist + Document List Component - rm saved searches data webscript + /slingshot/doclib/rm/savedsearches/site/{site}?p={public?} + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-savedsearches.get.js b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-savedsearches.get.js new file mode 100644 index 0000000000..76fa47dc7f --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-savedsearches.get.js @@ -0,0 +1,54 @@ +function main() +{ + var savedSearches = [], + siteId = url.templateArgs.site, + siteNode = siteService.getSite(siteId), + bPublic = args.p; + + if (siteNode === null) + { + status.setCode(status.STATUS_NOT_FOUND, "Site not found: '" + siteId + "'"); + return null; + } + + var searchNode = siteNode.getContainer("Saved Searches"); + if (searchNode != null) + { + var kids, ssNode; + + if (bPublic == null || bPublic == "true") + { + // public searches are in the root of the folder + kids = searchNode.children; + } + else + { + // user specific searches are in a sub-folder of username + var userNode = searchNode.childByNamePath(person.properties.userName); + if (userNode != null) + { + kids = userNode.children; + } + } + + if (kids) + { + for (var i = 0, ii = kids.length; i < ii; i++) + { + ssNode = kids[i]; + if (ssNode.isDocument) + { + savedSearches.push( + { + name: ssNode.name, + description: ssNode.properties.description + }); + } + } + } + } + + model.savedSearches = savedSearches; +} + +main(); \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-savedsearches.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-savedsearches.get.json.ftl new file mode 100644 index 0000000000..aab3d3a94f --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-savedsearches.get.json.ftl @@ -0,0 +1,13 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "items": + [ + <#list savedSearches as s> + { + "name": "${s.name}", + "description": "${s.description!""}" + }<#if s_has_next>, + + ] +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-transfer.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-transfer.get.desc.xml new file mode 100644 index 0000000000..03c9efc138 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-transfer.get.desc.xml @@ -0,0 +1,9 @@ + + doclist-transfer + Document List Component - rm transfer query data webscript + /slingshot/doclib/rm/transfer/node/{store_type}/{store_id}/{id} + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-transfer.get.js b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-transfer.get.js new file mode 100644 index 0000000000..a611074cf2 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-transfer.get.js @@ -0,0 +1,21 @@ +function main() +{ + var nodeRef = url.templateArgs.store_type + "://" + url.templateArgs.store_id + "/" + url.templateArgs.id, + transfer = search.findNode(nodeRef); + + if (transfer === null) + { + status.setCode(status.STATUS_NOT_FOUND, "Not a valid nodeRef: '" + nodeRef + "'"); + return null; + } + + if (String(transfer.typeShort) != "rma:transfer") + { + status.setCode(status.STATUS_BAD_REQUEST, "nodeRef: '" + nodeRef + "' is not of type 'rma:transfer'"); + return null; + } + + model.transfer = transfer; +} + +main(); \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-transfer.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-transfer.get.json.ftl new file mode 100644 index 0000000000..ff5ed41e60 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-transfer.get.json.ftl @@ -0,0 +1,13 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + <#if transfer??> + "transfer": + { + "nodeRef": "${transfer.nodeRef}", + "name": "${transfer.name}", + "rma:transferAccessionIndicator": ${(transfer.properties["rma:transferAccessionIndicator"]!false)?string}, + "rma:transferPDFIndicator": ${(transfer.properties["rma:transferPDFIndicator"]!false)?string} + } + +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-treenode.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-treenode.get.desc.xml new file mode 100644 index 0000000000..e7345e5692 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-treenode.get.desc.xml @@ -0,0 +1,12 @@ + + treenode + Document List Component - rm treenode data webscript + /slingshot/doclib/rm/treenode/site/{site}/{container}/{path} + /slingshot/doclib/rm/treenode/site/{site}/{container} + /slingshot/doclib/rm/treenode/node/{store_type}/{store_id}/{id}/{path} + /slingshot/doclib/rm/treenode/node/{store_type}/{store_id}/{id} + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-treenode.get.js b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-treenode.get.js new file mode 100644 index 0000000000..ebabc06382 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-treenode.get.js @@ -0,0 +1,130 @@ + + +/** + * Document List Component: treenode + */ +model.treenode = getTreenode(); + +/* Create collection of folders in the given space */ +function getTreenode() +{ + try + { + var items = new Array(), + hasSubfolders = true, + ignoredTypes = + { + "{http://www.alfresco.org/model/forum/1.0}forum": true, + "{http://www.alfresco.org/model/forum/1.0}topic": true, + "{http://www.alfresco.org/model/content/1.0}systemfolder": true + }, + skipPermissionCheck = args["perms"] == "false", + evalChildFolders = args["children"] !== "false", + item, rmNode, capabilities, cap; + + // Use helper function to get the arguments + var parsedArgs = ParseArgs.getParsedArgs(); + if (parsedArgs === null) + { + return; + } + + // Quick version if "skipPermissionCheck" flag set + if (skipPermissionCheck) + { + for each (item in parsedArgs.pathNode.children) + { + if (itemIsAllowed(item) && !(item.type in ignoredTypes)) + { + if (evalChildFolders) + { + hasSubfolders = item.childFileFolders(false, true, "fm:forum").length > 0; + } + + items.push( + { + node: item, + hasSubfolders: hasSubfolders + }); + } + } + } + else + { + for each (item in parsedArgs.pathNode.children) + { + if (itemIsAllowed(item) && !(item.type in ignoredTypes)) + { + capabilities = {}; + rmNode = rmService.getRecordsManagementNode(item); + for each (cap in rmNode.capabilitiesSet("Create")) + { + capabilities[cap.name] = true; + } + + if (evalChildFolders) + { + hasSubfolders = item.childFileFolders(false, true, "fm:forum").length > 0; + } + + items.push( + { + node: item, + hasSubfolders: hasSubfolders, + permissions: + { + create: capabilities["Create"] + } + }); + } + } + } + + items.sort(sortByName); + + return ( + { + parent: parsedArgs.pathNode, + resultsTrimmed: false, + items: items + }); + } + catch(e) + { + status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, e.toString()); + return; + } +} + + +/* Sort the results by case-insensitive name */ +function sortByName(a, b) +{ + return (b.node.name.toLowerCase() > a.node.name.toLowerCase() ? -1 : 1); +} + +/* Filter allowed types, etc. */ +function itemIsAllowed(item) +{ + // Must be a subtype of cm:folder + if (!item.isSubType("cm:folder")) + { + return false; + } + + var typeShort = String(item.typeShort); + + // Don't show Hold and Transfer top-level containers + if (typeShort == "rma:hold" || typeShort == "rma:transfer") + { + return false; + } + + // Must be a "dod:" or "rma:" namespaced type + if (typeShort.indexOf("dod:") !== 0 && typeShort.indexOf("rma") !== 0) + { + return false; + } + + return true; +} \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-treenode.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-treenode.get.json.ftl new file mode 100644 index 0000000000..e238c2279d --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-treenode.get.json.ftl @@ -0,0 +1,39 @@ +<#assign p = treenode.parent> +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "totalResults": ${treenode.items?size?c}, + "resultsTrimmed": ${treenode.resultsTrimmed?string}, + "parent": + { + "nodeRef": "${p.nodeRef}", + "userAccess": + { + "create": ${p.hasPermission("CreateChildren")?string}, + "edit": ${p.hasPermission("Write")?string}, + "delete": ${p.hasPermission("Delete")?string} + } + }, + "items": + [ + <#list treenode.items as item> + <#assign t = item.node> + { + <#if item.permissions??> + "userAccess": + { + <#list item.permissions?keys as perm> + <#if item.permissions[perm]?is_boolean> + "${perm?string}": ${item.permissions[perm]?string}<#if perm_has_next>, + + + }, + + "nodeRef": "${t.nodeRef}", + "name": "${t.name}", + "description": "${(t.properties.description!"")}", + "hasChildren": ${item.hasSubfolders?string} + }<#if item_has_next>, + + ] +} + diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/rmsearch/rmsavedsearches.delete.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/rmsearch/rmsavedsearches.delete.desc.xml new file mode 100644 index 0000000000..ea338656dc --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/rmsearch/rmsavedsearches.delete.desc.xml @@ -0,0 +1,9 @@ + + rmsavedsearches + RM Saved Searches + /slingshot/rmsavedsearches/site/{site}/{name} + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/rmsearch/rmsavedsearches.delete.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/rmsearch/rmsavedsearches.delete.json.ftl new file mode 100644 index 0000000000..576619debc --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/rmsearch/rmsavedsearches.delete.json.ftl @@ -0,0 +1,3 @@ +{ + "success": ${success?string} +} \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/rmsearch/rmsavedsearches.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/rmsearch/rmsavedsearches.get.desc.xml new file mode 100644 index 0000000000..f7824c206b --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/rmsearch/rmsavedsearches.get.desc.xml @@ -0,0 +1,9 @@ + + rmsavedsearches + RM Saved Searches + /slingshot/rmsavedsearches/site/{site}?p={public?} + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/rmsearch/rmsavedsearches.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/rmsearch/rmsavedsearches.get.json.ftl new file mode 100644 index 0000000000..e554900d31 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/rmsearch/rmsavedsearches.get.json.ftl @@ -0,0 +1,16 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "items": + [ + <#list savedSearches as s> + { + "name": "${s.name}", + "description": "${s.description!""}", + "query": "${s.query}", + "params": "${s.params}", + "sort": "${s.sort}" + }<#if s_has_next>, + + ] +} + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/rmsearch/rmsavedsearches.post.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/rmsearch/rmsavedsearches.post.desc.xml new file mode 100644 index 0000000000..dd07b5143c --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/rmsearch/rmsavedsearches.post.desc.xml @@ -0,0 +1,9 @@ + + rmsavedsearches + RM Saved Searches + /slingshot/rmsavedsearches/site/{site} + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/rmsearch/rmsavedsearches.post.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/rmsearch/rmsavedsearches.post.json.ftl new file mode 100644 index 0000000000..576619debc --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/rmsearch/rmsavedsearches.post.json.ftl @@ -0,0 +1,3 @@ +{ + "success": ${success?string} +} \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/rmsearch/rmsearch.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/rmsearch/rmsearch.get.desc.xml new file mode 100644 index 0000000000..c97310a712 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/rmsearch/rmsearch.get.desc.xml @@ -0,0 +1,9 @@ + + rmsearch + Record Search Component Data Webscript + /slingshot/rmsearch/{site}?query={query?}&sortby={sortby?}&filters={filters?}&maxitems={maxitems?} + argument + user + required + internal + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/rmsearch/rmsearch.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/rmsearch/rmsearch.get.json.ftl new file mode 100644 index 0000000000..2f64c6f7a3 --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/rmsearch/rmsearch.get.json.ftl @@ -0,0 +1,42 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "items": + [ + <#list items as item> + { + "nodeRef": "${item.nodeRef}", + "type": "${item.type}", + "name": "${item.name}", + "title": "${item.title!''}", + "description": "${item.description!''}", + "modifiedOn": "${xmldate(item.modifiedOn)}", + "modifiedByUser": "${item.modifiedByUser}", + "modifiedBy": "${item.modifiedBy}", + "createdOn": "${xmldate(item.createdOn)}", + "createdByUser": "${item.createdByUser}", + "createdBy": "${item.createdBy}", + "author": "${item.author!''}", + "size": ${item.size?c}, + <#if item.browseUrl??>"browseUrl": "${item.browseUrl}", + "parentFolder": "${item.parentFolder!""}", + "properties": + { + <#assign first=true> + <#list item.properties?keys as k> + <#if item.properties[k]??> + <#if !first>,<#else><#assign first=false>"${k}": + <#assign prop = item.properties[k]> + <#if prop?is_date>"${xmldate(prop)}" + <#elseif prop?is_boolean>${prop?string("true", "false")} + <#elseif prop?is_enumerable>[<#list prop as p>"${p}"<#if p_has_next>, ] + <#elseif prop?is_number>${prop?c} + <#else>"${prop}" + + + + } + }<#if item_has_next>, + + ] +} + \ No newline at end of file diff --git a/rm-server/gradle.properties b/rm-server/gradle.properties new file mode 100644 index 0000000000..1e54abeeba --- /dev/null +++ b/rm-server/gradle.properties @@ -0,0 +1,7 @@ +appName=rm +moduleid=org_alfresco_module_rm + +webAppName=alfresco +warFile=alfresco.war + +tomcatEnv=TOMCAT_HOME \ No newline at end of file diff --git a/rm-server/libs/postgresql-9.0-801.jdbc4.jar b/rm-server/libs/postgresql-9.0-801.jdbc4.jar new file mode 100644 index 0000000000..63e54165ec Binary files /dev/null and b/rm-server/libs/postgresql-9.0-801.jdbc4.jar differ diff --git a/rm-server/libs/spring-webscripts-1.0.0-tests.jar b/rm-server/libs/spring-webscripts-1.0.0-tests.jar new file mode 100644 index 0000000000..1b386b0743 Binary files /dev/null and b/rm-server/libs/spring-webscripts-1.0.0-tests.jar differ diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/FilePlanComponentKind.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/FilePlanComponentKind.java new file mode 100644 index 0000000000..3f7da216df --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/FilePlanComponentKind.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm; + +/** + * File plan component kind enumeration class. + *
+ * Helpful when trying to determine the characteristics of a kind + * of file plan component. + * + * @author Roy Wetherall + */ +public enum FilePlanComponentKind +{ + FILE_PLAN_COMPONENT, + FILE_PLAN, + RECORD_CATEGORY, + RECORD_FOLDER, + RECORD, + TRANSFER, + HOLD, + DISPOSITION_SCHEDULE; +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementAdminService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementAdminService.java new file mode 100644 index 0000000000..cc33374030 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementAdminService.java @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.module.org_alfresco_module_rm.caveat.RMListOfValuesConstraint.MatchLogic; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.ConstraintDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * Records management custom model service interface. Implementations of this class are responsible + * for the creation and maintenance of RM-related custom properties and custom associations. + * + * @author Neil McErlean, janv + */ +public interface RecordsManagementAdminService +{ + /** + * Initialise the custom model + */ + public void initialiseCustomModel(); + + /** + * Get a list of all registered customisable types and aspects. + * + * @return {@link Set}<{@link QName}> QName's of customisable types and aspects + */ + public Set getCustomisable(); + + /** + * Get a list of all the registered customisable types and aspects present on a given + * node reference. + * + * @param nodeRef node reference + * @return {@link Set}<{@link QName}> QName's of customisable types and aspects, empty if none + */ + public Set getCustomisable(NodeRef nodeRef); + + /** + * Indicates whether a type (or aspect) is customisable. + * + * @param type customisable type {@link QName} + * @return boolean true if type customisable, false otherwise + */ + public boolean isCustomisable(QName type); + + /** + * Makes a type customisable. + * + * @param type type {@link QName} to make customisable + */ + public void makeCustomisable(QName type); + + /** + * Assuming the custom properties are not in use, makes a type no longer customisable. + * + * @param type type {@link QName} to make customisable + */ + public void unmakeCustomisable(QName type); + + /** + * Indicates whether the custom property exists. + * + * @param property properties {@link QName} + * @return boolean true if property exists, false otherwise + */ + public boolean existsCustomProperty(QName property); + + /** + * This method returns the custom properties that have been defined for the specified + * customisable RM element. + *

+ * Note: the custom property definitions are retrieved from the dictionaryService + * which is notified of any newly created definitions on transaction commit. + * Therefore custom properties created in the current transaction will not appear + * in the result of this method. + * + * @param customisedElement + * @return + * @see CustomisableRmElement + */ + public Map getCustomPropertyDefinitions(QName customisableType); + + /** + * This method returns the custom properties that have been defined for all of + * the specified customisable RM elements. + * Note: the custom property definitions are retrieved from the dictionaryService + * which is notified of any newly created definitions on transaction commit. + * Therefore custom properties created in the current transaction will not appear + * in the result of this method. + * + * @return + * @see CustomisableRmElement + */ + public Map getCustomPropertyDefinitions(); + + /** + * Add custom property definition + * + * Note: no default value, single valued, optional, not system protected, no constraints + * + * @param propId - If a value for propId is provided it will be used to identify property definitions + * within URLs and in QNames. Therefore it must contain URL/QName-valid characters + * only. It must also be unique. + * If a null value is passed, an id will be generated. + * @param aspectName - mandatory. The aspect within which the property is to be defined. + * This must be one of the CustomisableRmElements. + * @param label - mandatory + * @param dataType - mandatory + * @param title - optional + * @param description - optional + * + * @return the propId, whether supplied as a parameter or generated. + * @see CustomisableRmElement#getCorrespondingAspect() + */ + public QName addCustomPropertyDefinition(QName propId, QName typeName, String label, QName dataType, String title, String description); + + /** + * Add custom property definition with one optional constraint reference + * + * @param propId - If a value for propId is provided it will be used to identify property definitions + * within URLs and in QNames. Therefore it must contain URL/QName-valid characters + * only. It must also be unique. + * If a null value is passed, an id will be generated. + * @param aspectName - mandatory. The aspect within which the property is to be defined. + * This must be one of the CustomisableRmElements. + * @param label - mandatory + * @param dataType - mandatory + * @param title - optional + * @param description - optional + * @param defaultValue - optional + * @param multiValued - TRUE if multi-valued property + * @param mandatory - TRUE if mandatory property + * @param isProtected - TRUE if protected property + * @param lovConstraintQName - optional custom constraint + * + * @return the propId, whether supplied as a parameter or generated. + * @see CustomisableRmElement#getCorrespondingAspect() + */ + + // TODO propId string (not QName) ? + // TODO remove title (since it is ignored) (or remove label to title) + + public QName addCustomPropertyDefinition(QName propId, QName typeName, String label, QName dataType, String title, String description, String defaultValue, boolean multiValued, boolean mandatory, boolean isProtected, QName lovConstraintQName); + + /** + * Update the custom property definition's label (title). + * + * @param propQName the qname of the property definition + * @param newLabel the new value for the label. + * @return the propId. + */ + public QName setCustomPropertyDefinitionLabel(QName propQName, String newLabel); + + /** + * Sets a new list of values constraint on the custom property definition. + * + * @param propQName the qname of the property definition + * @param newLovConstraint the List-Of-Values constraintRef. + * @return the propId. + */ + public QName setCustomPropertyDefinitionConstraint(QName propQName, QName newLovConstraint); + + /** + * Removes all list of values constraints from the custom property definition. + * + * @param propQName the qname of the property definition + * @return the propId. + */ + public QName removeCustomPropertyDefinitionConstraints(QName propQName); + + /** + * Remove custom property definition + * + * @param propQName + */ + public void removeCustomPropertyDefinition(QName propQName); + + /** + * This method returns the custom references that have been defined in the custom + * model. + * Note: the custom reference definitions are retrieved from the dictionaryService + * which is notified of any newly created definitions on transaction commit. + * Therefore custom references created in the current transaction will not appear + * in the results. + * + * @return The Map of custom references (both parent-child and standard). + */ + public Map getCustomReferenceDefinitions(); + + /** + * Fetches all associations from the given source. + * + * @param node the node from which the associations start. + * @return a List of associations. + */ + public List getCustomReferencesFrom(NodeRef node); + + /** + * Fetches all child associations of the given source. i.e. all associations where the + * given node is the parent. + * + * @param node + * @return + */ + public List getCustomChildReferences(NodeRef node); + + /** + * Returns a List of all associations to the given node. + * + * @param node the node to which the associations point. + * @return a List of associations. + */ + public List getCustomReferencesTo(NodeRef node); + + /** + * Fetches all child associations where the given node is the child. + * + * @param node + * @return + */ + public List getCustomParentReferences(NodeRef node); + + /** + * This method adds the specified custom reference instance between the specified nodes. + * Only one instance of any custom reference type is allowed in a given direction + * between two given records. + * + * @param fromNode + * @param toNode + * @param assocId the server-side qname e.g. {http://www.alfresco.org/model/rmcustom/1.0}abcd-12-efgh-4567 + * @throws AlfrescoRuntimeException if an instance of the specified reference type + * already exists from fromNode to toNode. + */ + public void addCustomReference(NodeRef fromNode, NodeRef toNode, QName assocId); + + /** + * This method removes the specified custom reference instance from the specified node. + * + * @param fromNode + * @param toNode + * @param assocId the server-side qname e.g. {http://www.alfresco.org/model/rmcustom/1.0}abcd-12-efgh-4567 + */ + public void removeCustomReference(NodeRef fromNode, NodeRef toNode, QName assocId); + + /** + * This method creates a new custom association, using the given label as the title. + * + * @param label the title of the association definition + * @return the QName of the newly-created association. + */ + public QName addCustomAssocDefinition(String label); + + /** + * This method creates a new custom child association, combining the given source and + * target and using the combined String as the title. + * + * @param source + * @param target + * @return the QName of the newly-created association. + */ + public QName addCustomChildAssocDefinition(String source, String target); + + /** + * This method updates the source and target values for the specified child association. + * The source and target will be combined into a single string and stored in the title property. + * Source and target are String metadata for RM parent/child custom references. + * + * @param refQName qname of the child association. + * @param newSource the new value for the source field. + * @param newTarget the new value for the target field. + * @see #getCompoundIdFor(String, String) + * @see #splitSourceTargetId(String) + */ + public QName updateCustomChildAssocDefinition(QName refQName, String newSource, String newTarget); + + /** + * This method updates the label value for the specified association. + * The label will be stored in the title property. + * Label is String metadata for bidirectional custom references. + * + * @param refQName qname of the child association. + * @param newLabel the new value for the label field. + */ + public QName updateCustomAssocDefinition(QName refQName, String newLabel); + + /** + * This method returns ConstraintDefinition objects defined in the given model + * (note: not property references or in-line defs) + * The custom constraint definitions are retrieved from the dictionaryService + * which is notified of any newly created definitions on transaction commit. + * Therefore custom constraints created in the current transaction will not appear + * in the results. + */ + public List getCustomConstraintDefinitions(QName modelQName); + + /** + * This method adds a Constraint definition to the custom model. + * The implementation of this method would have to go into the M2Model and insert + * the relevant M2Objects for this new constraint. + * + * param type not included as it would always be RMListOfValuesConstraint for RM. + * + * @param constraintName the name e.g. rmc:foo + * @param title the human-readable title e.g. My foo list + * @param caseSensitive + * @param allowedValues the allowed values list + * @param matchLogic AND (all values must match), OR (at least one values must match) + */ + public void addCustomConstraintDefinition(QName constraintName, String title, boolean caseSensitive, List allowedValues, MatchLogic matchLogic); + + /** + * Remove custom constraint definition - if not referenced (by any properties) + * + * + * @param constraintName the name e.g. rmc:foo + */ + public void removeCustomConstraintDefinition(QName constraintName); + + /** + * Update custom constraint definition with new list of values (replaces existing list, if any) + * + * @param constraintName the name e.g. rmc:foo + * @param newValues + */ + public void changeCustomConstraintValues(QName constraintName, List newValues); + + /** + * + * @param constraintName + * @param title + */ + public void changeCustomConstraintTitle(QName constraintName, String title); + + /** + * This method iterates over the custom properties, references looking for one whose id + * exactly matches that specified. + * + * @param localName the localName part of the qname of the property or reference definition. + * @return the QName of the property, association definition which matches, or null. + */ + public QName getQNameForClientId(String localName); + + /** + * Given a compound id for source and target strings (as used with parent/child + * custom references), this method splits the String and returns an array containing + * the source and target IDs separately. + * + * @param sourceTargetId the compound ID. + * @return a String array, where result[0] == sourceId and result[1] == targetId. + */ + public String[] splitSourceTargetId(String sourceTargetId); + + /** + * This method retrieves a compound ID (client-side) for the specified + * sourceId and targetId. + * + * @param sourceId + * @param targetId + * @return + */ + public String getCompoundIdFor(String sourceId, String targetId); +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementAdminServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementAdminServiceImpl.java new file mode 100644 index 0000000000..0b15d38b5d --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementAdminServiceImpl.java @@ -0,0 +1,1548 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.BeforeCreateReference; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.BeforeRemoveReference; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.OnCreateReference; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.OnRemoveReference; +import org.alfresco.module.org_alfresco_module_rm.caveat.RMListOfValuesConstraint; +import org.alfresco.module.org_alfresco_module_rm.caveat.RMListOfValuesConstraint.MatchLogic; +import org.alfresco.module.org_alfresco_module_rm.dod5015.DOD5015Model; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementCustomModel; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.dictionary.DictionaryRepositoryBootstrap; +import org.alfresco.repo.dictionary.IndexTokenisationMode; +import org.alfresco.repo.dictionary.M2Aspect; +import org.alfresco.repo.dictionary.M2Association; +import org.alfresco.repo.dictionary.M2ChildAssociation; +import org.alfresco.repo.dictionary.M2ClassAssociation; +import org.alfresco.repo.dictionary.M2Constraint; +import org.alfresco.repo.dictionary.M2Model; +import org.alfresco.repo.dictionary.M2Namespace; +import org.alfresco.repo.dictionary.M2Property; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.policy.ClassPolicyDelegate; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.policy.Behaviour.NotificationFrequency; +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.Constraint; +import org.alfresco.service.cmr.dictionary.ConstraintDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryException; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.util.GUID; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.surf.util.I18NUtil; +import org.springframework.extensions.surf.util.ParameterCheck; + +/** + * Records Management AdminService Implementation. + * + * @author Neil McErlean, janv + */ +public class RecordsManagementAdminServiceImpl implements RecordsManagementAdminService, + RecordsManagementCustomModel, + NodeServicePolicies.OnAddAspectPolicy, + NodeServicePolicies.OnRemoveAspectPolicy, + NodeServicePolicies.OnCreateNodePolicy +{ + /** Logger */ + private static Log logger = LogFactory.getLog(RecordsManagementAdminServiceImpl.class); + + /** I18N messages*/ + private static final String MSG_SERVICE_NOT_INIT = "rm.admin.service-not-init"; + private static final String MSG_NOT_CUSTOMISABLE = "rm.admin.not-customisable"; + private static final String MSG_INVALID_CUSTOM_ASPECT = "rm.admin.invalid-custom-aspect"; + private static final String MSG_PROPERTY_ALREADY_EXISTS = "rm.admin.property-already-exists"; + private static final String MSG_CANNOT_APPLY_CONSTRAINT = "rm.admin.cannot-apply-constraint"; + private static final String MSG_PROP_EXIST = "rm.admin.prop-exist"; + private static final String MSG_CUSTOM_PROP_EXIST = "rm.admin.custom-prop-exist"; + private static final String MSG_UNKNOWN_ASPECT = "rm.admin.unknown-aspect"; + private static final String MSG_REF_EXIST = "rm.admin.ref-exist"; + private static final String MSG_REF_LABEL_IN_USE = "rm.admin.ref-label-in-use"; + private static final String MSG_ASSOC_EXISTS = "rm.admin.assoc-exists"; + private static final String MSG_CHILD_ASSOC_EXISTS = "rm.admin.child-assoc-exists"; + private static final String MSG_CONNOT_FIND_ASSOC_DEF = "rm.admin.cannot-find-assoc-def"; + private static final String MSG_CONSTRAINT_EXISTS = "rm.admin.constraint-exists"; + private static final String MSG_CANNOT_FIND_CONSTRAINT = "rm.admin.contraint-cannot-find"; + private static final String MSG_UNEXPECTED_TYPE_CONSTRAINT = "rm.admin.unexpected_type_constraint"; + private static final String MSG_CUSTOM_MODEL_NOT_FOUND = "rm.admin.custom-model-not-found"; + private static final String MSG_CUSTOM_MODEL_NO_CONTENT = "rm.admin.custom-model-no-content"; + private static final String MSG_ERROR_WRITE_CUSTOM_MODEL = "rm.admin.error-write-custom-model"; + private static final String MSG_ERROR_CLIENT_ID = "rm.admin.error-client-id"; + private static final String MSG_ERROR_SPLIT_ID = "rm.admin.error-split-id"; + + /** Constants */ + public static final String RMC_CUSTOM_ASSOCS = RecordsManagementCustomModel.RM_CUSTOM_PREFIX + ":customAssocs"; + private static final String CUSTOM_CONSTRAINT_TYPE = org.alfresco.module.org_alfresco_module_rm.caveat.RMListOfValuesConstraint.class.getName(); + private static final NodeRef RM_CUSTOM_MODEL_NODE_REF = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "records_management_custom_model"); + private static final String PARAM_ALLOWED_VALUES = "allowedValues"; + private static final String PARAM_CASE_SENSITIVE = "caseSensitive"; + private static final String PARAM_MATCH_LOGIC = "matchLogic"; + public static final String RMA_RECORD = "rma:record"; + private static final String SOURCE_TARGET_ID_SEPARATOR = "__"; + + /** Dictionary service */ + private DictionaryService dictionaryService; + + /** Namespace service */ + private NamespaceService namespaceService; + + /** Node service */ + private NodeService nodeService; + + /** Content service */ + private ContentService contentService; + + /** Dictionary repository bootstrap */ + private DictionaryRepositoryBootstrap dictonaryRepositoryBootstrap; + + /** Policy component */ + private PolicyComponent policyComponent; + + /** Policy delegates */ + private ClassPolicyDelegate beforeCreateReferenceDelegate; + private ClassPolicyDelegate onCreateReferenceDelegate; + private ClassPolicyDelegate beforeRemoveReferenceDelegate; + private ClassPolicyDelegate onRemoveReferenceDelegate; + + /** List of types that can be customisable */ + private List pendingCustomisableTypes; + private Map customisableTypes; + + /** + * @param dictionaryService the dictionary service + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + /** + * @param namespaceService the namespace service + */ + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + /** + * @param nodeService the node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @param contentService the content service + */ + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + + /** + * @param policyComponent the policy component + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * Bootstrap for standard (non-RMC) dynamic models + * + * @param dictonaryRepositoryBootstrap dictionary repository bootstrap + */ + public void setDictionaryRepositoryBootstrap(DictionaryRepositoryBootstrap dictonaryRepositoryBootstrap) + { + this.dictonaryRepositoryBootstrap = dictonaryRepositoryBootstrap; + } + + /** + * Initialisation method + */ + public void init() + { + // Register the various policies + beforeCreateReferenceDelegate = policyComponent.registerClassPolicy(BeforeCreateReference.class); + onCreateReferenceDelegate = policyComponent.registerClassPolicy(OnCreateReference.class); + beforeRemoveReferenceDelegate = policyComponent.registerClassPolicy(BeforeRemoveReference.class); + onRemoveReferenceDelegate = policyComponent.registerClassPolicy(OnRemoveReference.class); + } + + protected void invokeBeforeCreateReference(NodeRef fromNodeRef, NodeRef toNodeRef, QName reference) + { + // get qnames to invoke against + Set qnames = RecordsManagementPoliciesUtil.getTypeAndAspectQNames(nodeService, fromNodeRef); + // execute policy for node type and aspects + BeforeCreateReference policy = beforeCreateReferenceDelegate.get(qnames); + policy.beforeCreateReference(fromNodeRef, toNodeRef, reference); + } + + protected void invokeOnCreateReference(NodeRef fromNodeRef, NodeRef toNodeRef, QName reference) + { + // get qnames to invoke against + Set qnames = RecordsManagementPoliciesUtil.getTypeAndAspectQNames(nodeService, fromNodeRef); + // execute policy for node type and aspects + OnCreateReference policy = onCreateReferenceDelegate.get(qnames); + policy.onCreateReference(fromNodeRef, toNodeRef, reference); + } + + protected void invokeBeforeRemoveReference(NodeRef fromNodeRef, NodeRef toNodeRef, QName reference) + { + // get qnames to invoke against + Set qnames = RecordsManagementPoliciesUtil.getTypeAndAspectQNames(nodeService, fromNodeRef); + // execute policy for node type and aspects + BeforeRemoveReference policy = beforeRemoveReferenceDelegate.get(qnames); + policy.beforeRemoveReference(fromNodeRef, toNodeRef, reference); + } + + protected void invokeOnRemoveReference(NodeRef fromNodeRef, NodeRef toNodeRef, QName reference) + { + // get qnames to invoke against + Set qnames = RecordsManagementPoliciesUtil.getTypeAndAspectQNames(nodeService, fromNodeRef); + // execute policy for node type and aspects + OnRemoveReference policy = onRemoveReferenceDelegate.get(qnames); + policy.onRemoveReference(fromNodeRef, toNodeRef, reference); + } + + @Override + public void onAddAspect(NodeRef nodeRef, QName aspectTypeQName) + { + if (nodeService.exists(nodeRef) == true && + isCustomisable(aspectTypeQName) == true) + { + QName customPropertyAspect = getCustomAspect(aspectTypeQName); + nodeService.addAspect(nodeRef, customPropertyAspect, null); + } + } + + @Override + public void onRemoveAspect(NodeRef nodeRef, QName aspectTypeQName) + { + if (nodeService.exists(nodeRef) == true && + isCustomisable(aspectTypeQName) == true) + { + QName customPropertyAspect = getCustomAspect(aspectTypeQName); + nodeService.removeAspect(nodeRef, customPropertyAspect); + } + } + + @Override + public void onCreateNode(ChildAssociationRef childAssocRef) + { + NodeRef nodeRef = childAssocRef.getChildRef(); + QName type = nodeService.getType(nodeRef); + while (type != null && ContentModel.TYPE_CMOBJECT.equals(type) == false) + { + if (isCustomisable(type) == true) + { + QName customPropertyAspect = getCustomAspect(type); + nodeService.addAspect(nodeRef, customPropertyAspect, null); + } + + TypeDefinition def = dictionaryService.getType(type); + if (def != null) + { + type = def.getParentName(); + } + else + { + type = null; + } + } + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService#initialiseCustomModel() + */ + public void initialiseCustomModel() + { + // Bind class behaviours + policyComponent.bindClassBehaviour( + NodeServicePolicies.OnAddAspectPolicy.QNAME, + this, + new JavaBehaviour(this, "onAddAspect", NotificationFrequency.FIRST_EVENT)); + policyComponent.bindClassBehaviour( + NodeServicePolicies.OnRemoveAspectPolicy.QNAME, + this, + new JavaBehaviour(this, "onRemoveAspect", NotificationFrequency.FIRST_EVENT)); + policyComponent.bindClassBehaviour( + NodeServicePolicies.OnCreateNodePolicy.QNAME, + this, + new JavaBehaviour(this, "onCreateNode", NotificationFrequency.FIRST_EVENT)); + + // Initialise the map + getCustomisableMap(); + } + + /** + * @param customisableTypes list of string representations of the type qnames that are customisable + */ + public void setCustomisableTypes(List customisableTypes) + { + pendingCustomisableTypes = new ArrayList(); + for (String customisableType : customisableTypes) + { + pendingCustomisableTypes.add(QName.createQName(customisableType, namespaceService)); + } + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService#getCustomisable() + */ + public Set getCustomisable() + { + return getCustomisableMap().keySet(); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService#getCustomisable(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public Set getCustomisable(NodeRef nodeRef) + { + Set result = new HashSet(5); + + // Check the nodes hierarchy for customisable types + QName type = nodeService.getType(nodeRef); + while (type != null && ContentModel.TYPE_CMOBJECT.equals(type) == false) + { + // Add to the list if the type is customisable + if (isCustomisable(type) == true) + { + result.add(type); + } + + // Type and get the types parent + TypeDefinition def = dictionaryService.getType(type); + if (def != null) + { + type = def.getParentName(); + } + else + { + type = null; + } + } + + // Get all the nodes aspects + Set aspects = nodeService.getAspects(nodeRef); + for (QName aspect : aspects) + { + QName tempAspect = QName.createQName(aspect.toString()); + while (tempAspect != null) + { + // Add to the list if the aspect is customisable + if (isCustomisable(tempAspect) == true) + { + result.add(tempAspect); + } + + // Try and get the parent aspect + AspectDefinition aspectDef = dictionaryService.getAspect(tempAspect); + if (aspectDef != null) + { + tempAspect = aspectDef.getParentName(); + } + else + { + tempAspect = null; + } + } + } + + return result; + } + + /** + * Gets a map containing all the customisable types + * + * @return map from the customisable type to its custom aspect + */ + private Map getCustomisableMap() + { + if (customisableTypes == null) + { + customisableTypes = new HashMap(7); + Collection aspects = dictionaryService.getAspects(RM_CUSTOM_MODEL); + for (QName aspect : aspects) + { + AspectDefinition aspectDef = dictionaryService.getAspect(aspect); + String name = aspectDef.getName().getLocalName(); + if (name.endsWith("Properties") == true) + { + QName type = null; + String prefixString = aspectDef.getDescription(); + if (prefixString == null) + { + // Backward compatibility from previous RM V1.0 custom models + if ("customRecordProperties".equals(name) == true) + { + type = RecordsManagementModel.ASPECT_RECORD; + } + else if ("customRecordFolderProperties".equals(name) == true) + { + type = RecordsManagementModel.TYPE_RECORD_FOLDER; + } + else if ("customRecordCategoryProperties".equals(name) == true) + { + type = RecordsManagementModel.TYPE_RECORD_CATEGORY; + } + else if ("customRecordSeriesProperties".equals(name) == true) + { + // Only add the deprecated record series type as customisable if + // a v1.0 installation has added custom properties + if (aspectDef.getProperties().size() != 0) + { + type = DOD5015Model.TYPE_RECORD_SERIES; + } + } + } + else + { + type = QName.createQName(prefixString, namespaceService); + } + + // Add the customisable type to the map + if (type != null) + { + customisableTypes.put(type, aspect); + + // Remove customisable type from the pending list + if (pendingCustomisableTypes != null && pendingCustomisableTypes.contains(type) == true) + { + pendingCustomisableTypes.remove(type); + } + } + } + } + + // Deal with any pending types left over + if (pendingCustomisableTypes != null && pendingCustomisableTypes.size() != 0) + { + NodeRef modelRef = getCustomModelRef(RecordsManagementModel.RM_CUSTOM_URI); + M2Model model = readCustomContentModel(modelRef); + try + { + for (QName customisableType : pendingCustomisableTypes) + { + QName customAspect = getCustomAspectImpl(customisableType); + + // Create the new aspect to hold the custom properties + M2Aspect aspect = model.createAspect(customAspect.toPrefixString(namespaceService)); + aspect.setDescription(customisableType.toPrefixString(namespaceService)); + + // Make a record of the customisable type + customisableTypes.put(customisableType, customAspect); + } + } + finally + { + writeCustomContentModel(modelRef, model); + } + } + } + return customisableTypes; + } + + /** + * Gets the QName of the custom aspect given the customisable type QName + * + * @param customisableType + * @return + */ + private QName getCustomAspect(QName customisableType) + { + Map map = getCustomisableMap(); + QName result = map.get(customisableType); + if (result == null) + { + result = getCustomAspectImpl(customisableType); + } + return result; + } + + /** + * Builds a custom aspect QName from a customisable type/aspect QName + * + * @param customisableType + * @return + */ + private QName getCustomAspectImpl(QName customisableType) + { + String localName = customisableType.toPrefixString(namespaceService).replace(":", ""); + localName = MessageFormat.format("{0}CustomProperties", localName); + return QName.createQName(RM_CUSTOM_URI, localName); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService#isCustomisable(org.alfresco.service.namespace.QName) + */ + @Override + public boolean isCustomisable(QName type) + { + ParameterCheck.mandatory("type", type); + return getCustomisable().contains(type); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService#makeCustomisable(org.alfresco.service.namespace.QName) + */ + @Override + public void makeCustomisable(QName type) + { + ParameterCheck.mandatory("type", type); + + if (customisableTypes == null) + { + // Add the type to the pending list + pendingCustomisableTypes.add(type); + } + else + { + QName customAspect = getCustomAspect(type); + if (dictionaryService.getAspect(customAspect) == null) + { + NodeRef modelRef = getCustomModelRef(customAspect.getNamespaceURI()); + M2Model model = readCustomContentModel(modelRef); + try + { + // Create the new aspect to hold the custom properties + M2Aspect aspect = model.createAspect(customAspect.toPrefixString(namespaceService)); + aspect.setDescription(type.toPrefixString(namespaceService)); + } + finally + { + writeCustomContentModel(modelRef, model); + } + customisableTypes.put(type, customAspect); + } + } + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService#unmakeCustomisable(org.alfresco.service.namespace.QName) + */ + @Override + public void unmakeCustomisable(QName type) + { + ParameterCheck.mandatory("type", type); + + if (customisableTypes == null) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_SERVICE_NOT_INIT)); + } + + QName customAspect = getCustomAspect(type); + if (dictionaryService.getAspect(customAspect) != null) + { + // TODO need to confirm that the custom properties are not being used! + + NodeRef modelRef = getCustomModelRef(customAspect.getNamespaceURI()); + M2Model model = readCustomContentModel(modelRef); + try + { + // Create the new aspect to hold the custom properties + model.removeAspect(customAspect.toPrefixString(namespaceService)); + } + finally + { + writeCustomContentModel(modelRef, model); + } + customisableTypes.remove(type); + } + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService#existsCustomProperty(org.alfresco.service.namespace.QName) + */ + @Override + public boolean existsCustomProperty(QName propertyName) + { + ParameterCheck.mandatory("propertyName", propertyName); + + boolean result = false; + if (RM_CUSTOM_URI.equals(propertyName.getNamespaceURI()) == true && + dictionaryService.getProperty(propertyName) != null) + { + result = true; + } + return result; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService#getCustomPropertyDefinitions() + */ + public Map getCustomPropertyDefinitions() + { + Map result = new HashMap(); + for (QName customisableType : getCustomisable()) + { + Map props = getCustomPropertyDefinitions(customisableType); + if (props != null) + { + result.putAll(props); + } + } + return result; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService#getCustomPropertyDefinitions(org.alfresco.module.org_alfresco_module_rm.CustomisableRmElement) + */ + public Map getCustomPropertyDefinitions(QName customisableType) + { + Map propDefns = null; + QName relevantAspectQName = getCustomAspect(customisableType); + AspectDefinition aspectDefn = dictionaryService.getAspect(relevantAspectQName); + if (aspectDefn != null) + { + propDefns = aspectDefn.getProperties(); + } + + return propDefns; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService#addCustomPropertyDefinition(org.alfresco.service.namespace.QName, org.alfresco.service.namespace.QName, java.lang.String, org.alfresco.service.namespace.QName, java.lang.String, java.lang.String) + */ + public QName addCustomPropertyDefinition(QName propId, QName aspectName, String label, QName dataType, String title, String description) + { + return addCustomPropertyDefinition(propId, aspectName, label, dataType, title, description, null, false, false, false, null); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService#addCustomPropertyDefinition(org.alfresco.service.namespace.QName, org.alfresco.service.namespace.QName, java.lang.String, org.alfresco.service.namespace.QName, java.lang.String, java.lang.String, java.lang.String, boolean, boolean, boolean, org.alfresco.service.namespace.QName) + */ + public QName addCustomPropertyDefinition(QName propId, QName aspectName, String label, QName dataType, String title, String description, String defaultValue, boolean multiValued, boolean mandatory, boolean isProtected, QName lovConstraint) + { + if (isCustomisable(aspectName) == false) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NOT_CUSTOMISABLE, aspectName.toPrefixString(namespaceService))); + } + + // title parameter is currently ignored. Intentionally. + if (propId == null) + { + // Generate a propId + propId = this.generateQNameFor(label); + } + + ParameterCheck.mandatory("aspectName", aspectName); + ParameterCheck.mandatory("label", label); + ParameterCheck.mandatory("dataType", dataType); + + NodeRef modelRef = getCustomModelRef(propId.getNamespaceURI()); + M2Model deserializedModel = readCustomContentModel(modelRef); + + QName customAspect = getCustomAspect(aspectName); + M2Aspect customPropsAspect = deserializedModel.getAspect(customAspect.toPrefixString(namespaceService)); + + if (customPropsAspect == null) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_INVALID_CUSTOM_ASPECT, customAspect, aspectName.toPrefixString(namespaceService))); + } + + String propIdAsString = propId.toPrefixString(namespaceService); + M2Property customProp = customPropsAspect.getProperty(propIdAsString); + if (customProp != null) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_PROPERTY_ALREADY_EXISTS, propIdAsString)); + } + + M2Property newProp = customPropsAspect.createProperty(propIdAsString); + newProp.setName(propIdAsString); + newProp.setType(dataType.toPrefixString(namespaceService)); + + // Note that the title is used to store the RM 'label'. + newProp.setTitle(label); + newProp.setDescription(description); + newProp.setDefaultValue(defaultValue); + + newProp.setMandatory(mandatory); + newProp.setProtected(isProtected); + newProp.setMultiValued(multiValued); + + newProp.setIndexed(true); + newProp.setIndexedAtomically(true); + newProp.setStoredInIndex(false); + newProp.setIndexTokenisationMode(IndexTokenisationMode.FALSE); + + if (lovConstraint != null) + { + if (! dataType.equals(DataTypeDefinition.TEXT)) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_CANNOT_APPLY_CONSTRAINT, lovConstraint, propIdAsString, dataType)); + } + + String lovConstraintQNameAsString = lovConstraint.toPrefixString(namespaceService); + newProp.addConstraintRef(lovConstraintQNameAsString); + } + + writeCustomContentModel(modelRef, deserializedModel); + + if (logger.isInfoEnabled()) + { + logger.info("addCustomPropertyDefinition: "+label+ + "=" + propIdAsString + " to aspect: "+aspectName); + } + + return propId; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService#setCustomPropertyDefinitionLabel(org.alfresco.service.namespace.QName, java.lang.String) + */ + public QName setCustomPropertyDefinitionLabel(QName propQName, String newLabel) + { + ParameterCheck.mandatory("propQName", propQName); + + PropertyDefinition propDefn = dictionaryService.getProperty(propQName); + if (propDefn == null) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_PROP_EXIST, propQName)); + } + + if (newLabel == null) return propQName; + + NodeRef modelRef = getCustomModelRef(propQName.getNamespaceURI()); + M2Model deserializedModel = readCustomContentModel(modelRef); + + M2Property targetProperty = findProperty(propQName, deserializedModel); + + targetProperty.setTitle(newLabel); + writeCustomContentModel(modelRef, deserializedModel); + + if (logger.isInfoEnabled()) + { + logger.info("setCustomPropertyDefinitionLabel: "+propQName+ + "=" + newLabel); + } + + return propQName; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService#setCustomPropertyDefinitionConstraint(org.alfresco.service.namespace.QName, org.alfresco.service.namespace.QName) + */ + public QName setCustomPropertyDefinitionConstraint(QName propQName, QName newLovConstraint) + { + ParameterCheck.mandatory("propQName", propQName); + + PropertyDefinition propDefn = dictionaryService.getProperty(propQName); + if (propDefn == null) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_PROP_EXIST, propQName)); + } + + NodeRef modelRef = getCustomModelRef(propQName.getNamespaceURI()); + M2Model deserializedModel = readCustomContentModel(modelRef); + + M2Property targetProp = findProperty(propQName, deserializedModel); + String dataType = targetProp.getType(); + + if (! dataType.equals(DataTypeDefinition.TEXT.toPrefixString(namespaceService))) + { + + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_CANNOT_APPLY_CONSTRAINT, newLovConstraint, targetProp.getName(), dataType)); + } + String lovConstraintQNameAsString = newLovConstraint.toPrefixString(namespaceService); + + // Add the constraint - if it isn't already there. + String refOfExistingConstraint = null; + + for (M2Constraint c : targetProp.getConstraints()) + { + // There should only be one constraint. + refOfExistingConstraint = c.getRef(); + break; + } + if (refOfExistingConstraint != null) + { + targetProp.removeConstraintRef(refOfExistingConstraint); + } + targetProp.addConstraintRef(lovConstraintQNameAsString); + + writeCustomContentModel(modelRef, deserializedModel); + + if (logger.isInfoEnabled()) + { + logger.info("addCustomPropertyDefinitionConstraint: "+lovConstraintQNameAsString); + } + + return propQName; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService#removeCustomPropertyDefinitionConstraints(org.alfresco.service.namespace.QName) + */ + public QName removeCustomPropertyDefinitionConstraints(QName propQName) + { + ParameterCheck.mandatory("propQName", propQName); + + PropertyDefinition propDefn = dictionaryService.getProperty(propQName); + if (propDefn == null) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_PROP_EXIST, propQName)); + } + + NodeRef modelRef = getCustomModelRef(propQName.getNamespaceURI()); + M2Model deserializedModel = readCustomContentModel(modelRef); + + M2Property targetProperty = findProperty(propQName, deserializedModel); + + // Need to count backwards to remove constraints + for (int i = targetProperty.getConstraints().size() - 1; i >= 0; i--) { + String ref = targetProperty.getConstraints().get(i).getRef(); + targetProperty.removeConstraintRef(ref); + } + + writeCustomContentModel(modelRef, deserializedModel); + + if (logger.isInfoEnabled()) + { + logger.info("removeCustomPropertyDefinitionConstraints: "+propQName); + } + + return propQName; + } + + /** + * + * @param propQName + * @param deserializedModel + * @return + */ + private M2Property findProperty(QName propQName, M2Model deserializedModel) + { + List aspects = deserializedModel.getAspects(); + // Search through the aspects looking for the custom property + for (M2Aspect aspect : aspects) + { + for (M2Property prop : aspect.getProperties()) + { + if (propQName.toPrefixString(namespaceService).equals(prop.getName())) + { + return prop; + } + } + } + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_CUSTOM_PROP_EXIST, propQName)); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService#removeCustomPropertyDefinition(org.alfresco.service.namespace.QName) + */ + public void removeCustomPropertyDefinition(QName propQName) + { + ParameterCheck.mandatory("propQName", propQName); + + NodeRef modelRef = getCustomModelRef(propQName.getNamespaceURI()); + M2Model deserializedModel = readCustomContentModel(modelRef); + + String propQNameAsString = propQName.toPrefixString(namespaceService); + + String aspectName = null; + + boolean found = false; + + // Need to select the correct aspect in the customModel from which we'll + // attempt to delete the property definition. + for (QName customisableType : getCustomisable()) + { + aspectName = getCustomAspect(customisableType).toPrefixString(namespaceService); + M2Aspect customPropsAspect = deserializedModel.getAspect(aspectName); + + if (customPropsAspect == null) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_UNKNOWN_ASPECT, aspectName)); + } + + M2Property prop = customPropsAspect.getProperty(propQNameAsString); + if (prop != null) + { + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Attempting to delete custom property: "); + msg.append(propQNameAsString); + logger.debug(msg.toString()); + } + + found = true; + customPropsAspect.removeProperty(propQNameAsString); + break; + } + } + + if (found == false) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_PROP_EXIST, propQNameAsString)); + } + + writeCustomContentModel(modelRef, deserializedModel); + + if (logger.isInfoEnabled()) + { + logger.info("deleteCustomPropertyDefinition: "+propQNameAsString+" from aspect: "+aspectName); + } + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService#getCustomReferenceDefinitions() + */ + public Map getCustomReferenceDefinitions() + { + QName relevantAspectQName = QName.createQName(RMC_CUSTOM_ASSOCS, namespaceService); + AspectDefinition aspectDefn = dictionaryService.getAspect(relevantAspectQName); + Map assocDefns = aspectDefn.getAssociations(); + + return assocDefns; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService#addCustomReference(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void addCustomReference(NodeRef fromNode, NodeRef toNode, QName refId) + { + // Check that a definition for the reference type exists. + Map availableAssocs = this.getCustomReferenceDefinitions(); + + AssociationDefinition assocDef = availableAssocs.get(refId); + if (assocDef == null) + { + throw new IllegalArgumentException(I18NUtil.getMessage(MSG_REF_EXIST, refId)); + } + + // Check if an instance of this reference type already exists in the same direction. + boolean associationAlreadyExists = false; + if (assocDef.isChild()) + { + List childAssocs = nodeService.getChildAssocs(fromNode, assocDef.getName(), assocDef.getName()); + for (ChildAssociationRef chAssRef : childAssocs) + { + if (chAssRef.getChildRef().equals(toNode)) + { + associationAlreadyExists = true; + } + } + } + else + { + List assocs = nodeService.getTargetAssocs(fromNode, assocDef.getName()); + for (AssociationRef assRef : assocs) + { + if (assRef.getTargetRef().equals(toNode)) + { + associationAlreadyExists = true; + } + } + } + if (associationAlreadyExists) + { + StringBuilder msg = new StringBuilder(); + msg.append("Association '").append(refId).append("' already exists from ") + .append(fromNode).append(" to ").append(toNode); + throw new AlfrescoRuntimeException(msg.toString()); + } + + // Invoke before create reference policy + invokeBeforeCreateReference(fromNode, toNode, refId); + + if (assocDef.isChild()) + { + this.nodeService.addChild(fromNode, toNode, refId, refId); + } + else + { + this.nodeService.createAssociation(fromNode, toNode, refId); + } + + // Invoke on create reference policy + invokeOnCreateReference(fromNode, toNode, refId); + } + + public void removeCustomReference(NodeRef fromNode, NodeRef toNode, QName assocId) + { + Map availableAssocs = this.getCustomReferenceDefinitions(); + + AssociationDefinition assocDef = availableAssocs.get(assocId); + if (assocDef == null) + { + throw new IllegalArgumentException(I18NUtil.getMessage(MSG_REF_EXIST, assocId)); + } + + invokeBeforeRemoveReference(fromNode, toNode, assocId); + + if (assocDef.isChild()) + { + // TODO: Ask for a more efficient method such as + // nodeService.removeChildAssociation(fromNode, toNode, chRef.getTypeQName(), null); + + List children = nodeService.getChildAssocs(fromNode); + for (ChildAssociationRef chRef : children) + { + if (assocId.equals(chRef.getTypeQName()) && chRef.getChildRef().equals(toNode)) + { + nodeService.removeChildAssociation(chRef); + } + } + } + else + { + nodeService.removeAssociation(fromNode, toNode, assocId); + } + + invokeOnRemoveReference(fromNode, toNode, assocId); + } + + public List getCustomReferencesFrom(NodeRef node) + { + List retrievedAssocs = nodeService.getTargetAssocs(node, RegexQNamePattern.MATCH_ALL); + return retrievedAssocs; + } + + public List getCustomChildReferences(NodeRef node) + { + List childAssocs = nodeService.getChildAssocs(node); + return childAssocs; + } + + public List getCustomReferencesTo(NodeRef node) + { + List retrievedAssocs = nodeService.getSourceAssocs(node, RegexQNamePattern.MATCH_ALL); + return retrievedAssocs; + } + + public List getCustomParentReferences(NodeRef node) + { + List result = nodeService.getParentAssocs(node); + return result; + } + + // note: currently RMC custom assocs only + public QName addCustomAssocDefinition(String label) + { + ParameterCheck.mandatoryString("label", label); + + NodeRef modelRef = getCustomModelRef(""); // defaults to RM_CUSTOM_URI + M2Model deserializedModel = readCustomContentModel(modelRef); + + String aspectName = RecordsManagementAdminServiceImpl.RMC_CUSTOM_ASSOCS; + + M2Aspect customAssocsAspect = deserializedModel.getAspect(aspectName); + + if (customAssocsAspect == null) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_UNKNOWN_ASPECT, aspectName)); + } + + // If this label is already taken... + if (getQNameForClientId(label) != null) + { + throw new IllegalArgumentException(I18NUtil.getMessage(MSG_REF_LABEL_IN_USE, label)); + } + + QName generatedQName = this.generateQNameFor(label); + String generatedShortQName = generatedQName.toPrefixString(namespaceService); + + M2ClassAssociation customAssoc = customAssocsAspect.getAssociation(generatedShortQName); + if (customAssoc != null) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_ASSOC_EXISTS, generatedShortQName)); + } + + M2Association newAssoc = customAssocsAspect.createAssociation(generatedShortQName); + newAssoc.setSourceMandatory(false); + newAssoc.setTargetMandatory(false); + + // MOB-1573 + newAssoc.setSourceMany(true); + newAssoc.setTargetMany(true); + + // The label is stored in the title. + newAssoc.setTitle(label); + + // TODO Could be the customAssocs aspect + newAssoc.setTargetClassName(RecordsManagementAdminServiceImpl.RMA_RECORD); + + writeCustomContentModel(modelRef, deserializedModel); + + if (logger.isInfoEnabled()) + { + logger.info("addCustomAssocDefinition: ("+label+")"); + } + + return generatedQName; + } + + // note: currently RMC custom assocs only + public QName addCustomChildAssocDefinition(String source, String target) + { + ParameterCheck.mandatoryString("source", source); + ParameterCheck.mandatoryString("target", target); + + NodeRef modelRef = getCustomModelRef(""); // defaults to RM_CUSTOM_URI + M2Model deserializedModel = readCustomContentModel(modelRef); + + String aspectName = RecordsManagementAdminServiceImpl.RMC_CUSTOM_ASSOCS; + + M2Aspect customAssocsAspect = deserializedModel.getAspect(aspectName); + + if (customAssocsAspect == null) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_UNKNOWN_ASPECT, aspectName)); + } + + String compoundID = this.getCompoundIdFor(source, target); + if (getQNameForClientId(compoundID) != null) + { + throw new IllegalArgumentException(I18NUtil.getMessage(MSG_REF_LABEL_IN_USE, compoundID)); + } + + M2ClassAssociation customAssoc = customAssocsAspect.getAssociation(compoundID); + if (customAssoc != null) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_CHILD_ASSOC_EXISTS, compoundID)); + } + QName generatedQName = this.generateQNameFor(compoundID); + + M2ChildAssociation newAssoc = customAssocsAspect.createChildAssociation(generatedQName.toPrefixString(namespaceService)); + newAssoc.setSourceMandatory(false); + newAssoc.setTargetMandatory(false); + + // MOB-1573 + newAssoc.setSourceMany(true); + newAssoc.setTargetMany(true); + + // source and target are stored in title. + newAssoc.setTitle(compoundID); + + // TODO Could be the custom assocs aspect + newAssoc.setTargetClassName(RecordsManagementAdminServiceImpl.RMA_RECORD); + + writeCustomContentModel(modelRef, deserializedModel); + + if (logger.isInfoEnabled()) + { + logger.info("addCustomChildAssocDefinition: ("+source+","+target+")"); + } + + return generatedQName; + } + + // note: currently RMC custom assocs only + public QName updateCustomChildAssocDefinition(QName refQName, String newSource, String newTarget) + { + String compoundId = getCompoundIdFor(newSource, newTarget); + return persistUpdatedAssocTitle(refQName, compoundId); + } + + // note: currently RMC custom assocs only + public QName updateCustomAssocDefinition(QName refQName, String newLabel) + { + return persistUpdatedAssocTitle(refQName, newLabel); + } + + /** + * This method writes the specified String into the association's title property. + * For RM custom properties and references, Title is used to store the identifier. + */ + // note: currently RMC custom assocs only + private QName persistUpdatedAssocTitle(QName refQName, String newTitle) + { + ParameterCheck.mandatory("refQName", refQName); + + AssociationDefinition assocDefn = dictionaryService.getAssociation(refQName); + if (assocDefn == null) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_CONNOT_FIND_ASSOC_DEF, refQName)); + } + + NodeRef modelRef = getCustomModelRef(""); // defaults to RM_CUSTOM_URI + M2Model deserializedModel = readCustomContentModel(modelRef); + + M2Aspect customAssocsAspect = deserializedModel.getAspect(RMC_CUSTOM_ASSOCS); + + for (M2ClassAssociation assoc : customAssocsAspect.getAssociations()) + { + if (refQName.toPrefixString(namespaceService).equals(assoc.getName())) + { + if (newTitle != null) + { + assoc.setTitle(newTitle); + } + } + } + writeCustomContentModel(modelRef, deserializedModel); + + if (logger.isInfoEnabled()) + { + logger.info("persistUpdatedAssocTitle: "+refQName+ + "=" + newTitle + " to aspect: " + RMC_CUSTOM_ASSOCS); + } + + return refQName; + } + + public void addCustomConstraintDefinition(QName constraintName, String title, boolean caseSensitive, List allowedValues, MatchLogic matchLogic) + { + ParameterCheck.mandatory("constraintName", constraintName); + ParameterCheck.mandatoryString("title", title); + ParameterCheck.mandatory("allowedValues", allowedValues); + + NodeRef modelRef = getCustomModelRef(constraintName.getNamespaceURI()); + M2Model deserializedModel = readCustomContentModel(modelRef); + + String constraintNameAsPrefixString = constraintName.toPrefixString(namespaceService); + + M2Constraint customConstraint = deserializedModel.getConstraint(constraintNameAsPrefixString); + if (customConstraint != null) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_CONSTRAINT_EXISTS, constraintNameAsPrefixString)); + } + + M2Constraint newCon = deserializedModel.createConstraint(constraintNameAsPrefixString, CUSTOM_CONSTRAINT_TYPE); + + newCon.setTitle(title); + newCon.createParameter(PARAM_ALLOWED_VALUES, allowedValues); + newCon.createParameter(PARAM_CASE_SENSITIVE, caseSensitive ? "true" : "false"); + newCon.createParameter(PARAM_MATCH_LOGIC, matchLogic.toString()); + + writeCustomContentModel(modelRef, deserializedModel); + + if (logger.isInfoEnabled()) + { + logger.info("addCustomConstraintDefinition: "+constraintNameAsPrefixString+" (valueCnt: "+allowedValues.size()+")"); + } + } + + /* + public void addCustomConstraintDefinition(QName constraintName, String description, Map parameters) + { + // TODO Auto-generated method stub + } + */ + + public void changeCustomConstraintValues(QName constraintName, List newAllowedValues) + { + ParameterCheck.mandatory("constraintName", constraintName); + ParameterCheck.mandatory("newAllowedValues", newAllowedValues); + + NodeRef modelRef = getCustomModelRef(constraintName.getNamespaceURI()); + M2Model deserializedModel = readCustomContentModel(modelRef); + + String constraintNameAsPrefixString = constraintName.toPrefixString(namespaceService); + + M2Constraint customConstraint = deserializedModel.getConstraint(constraintNameAsPrefixString); + if (customConstraint == null) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_CANNOT_FIND_CONSTRAINT, constraintNameAsPrefixString)); + } + + String type = customConstraint.getType(); + if ((type == null) || (! type.equals(CUSTOM_CONSTRAINT_TYPE))) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_UNEXPECTED_TYPE_CONSTRAINT, type, constraintNameAsPrefixString, CUSTOM_CONSTRAINT_TYPE)); + } + + customConstraint.removeParameter(PARAM_ALLOWED_VALUES); + customConstraint.createParameter(PARAM_ALLOWED_VALUES, newAllowedValues); + + writeCustomContentModel(modelRef, deserializedModel); + + if (logger.isInfoEnabled()) + { + logger.info("changeCustomConstraintValues: "+constraintNameAsPrefixString+" (valueCnt: "+newAllowedValues.size()+")"); + } + } + + public void changeCustomConstraintTitle(QName constraintName, String title) + { + ParameterCheck.mandatory("constraintName", constraintName); + ParameterCheck.mandatoryString("title", title); + + NodeRef modelRef = getCustomModelRef(constraintName.getNamespaceURI()); + M2Model deserializedModel = readCustomContentModel(modelRef); + + String constraintNameAsPrefixString = constraintName.toPrefixString(namespaceService); + + M2Constraint customConstraint = deserializedModel.getConstraint(constraintNameAsPrefixString); + if (customConstraint == null) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_CANNOT_FIND_CONSTRAINT, constraintNameAsPrefixString)); + } + + String type = customConstraint.getType(); + if ((type == null) || (! type.equals(CUSTOM_CONSTRAINT_TYPE))) + { + + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_UNEXPECTED_TYPE_CONSTRAINT, type, constraintNameAsPrefixString, CUSTOM_CONSTRAINT_TYPE)); + } + + customConstraint.setTitle(title); + + writeCustomContentModel(modelRef, deserializedModel); + + if (logger.isInfoEnabled()) + { + logger.info("changeCustomConstraintTitle: "+constraintNameAsPrefixString+" (title: "+title+")"); + } + } + + public List getCustomConstraintDefinitions(QName modelQName) + { + Collection conDefs = dictionaryService.getConstraints(modelQName, true); + + for (ConstraintDefinition conDef : conDefs) + { + Constraint con = conDef.getConstraint(); + if (! (con instanceof RMListOfValuesConstraint)) + { + conDefs.remove(conDef); + } + } + + return new ArrayList(conDefs); + } + + public void removeCustomConstraintDefinition(QName constraintName) + { + ParameterCheck.mandatory("constraintName", constraintName); + + NodeRef modelRef = getCustomModelRef(constraintName.getNamespaceURI()); + M2Model deserializedModel = readCustomContentModel(modelRef); + + String constraintNameAsPrefixString = constraintName.toPrefixString(namespaceService); + + M2Constraint customConstraint = deserializedModel.getConstraint(constraintNameAsPrefixString); + if (customConstraint == null) + { + + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_CANNOT_FIND_CONSTRAINT, constraintNameAsPrefixString)); + } + + deserializedModel.removeConstraint(constraintNameAsPrefixString); + + writeCustomContentModel(modelRef, deserializedModel); + + if (logger.isInfoEnabled()) + { + logger.info("deleteCustomConstraintDefinition: "+constraintNameAsPrefixString); + } + } + + private NodeRef getCustomModelRef(String uri) + { + if ((uri.equals("")) || (uri.equals(RecordsManagementModel.RM_CUSTOM_URI))) + { + // note: short-cut for "rmc" currently assumes that RM custom model does not define additional namespaces + return RM_CUSTOM_MODEL_NODE_REF; + } + else + { + // ALF-5875 + List modelRefs = dictonaryRepositoryBootstrap.getModelRefs(); + + for (NodeRef modelRef : modelRefs) + { + try + { + M2Model model = readCustomContentModel(modelRef); + + for (M2Namespace namespace : model.getNamespaces()) + { + if (namespace.getUri().equals(uri)) + { + return modelRef; + } + } + } + catch (DictionaryException de) + { + logger.warn("readCustomContentModel: skip model ("+modelRef+") whilst searching for uri ("+uri+"): "+de); + } + } + + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_CUSTOM_MODEL_NOT_FOUND, uri)); + } + } + + private M2Model readCustomContentModel(NodeRef modelNodeRef) + { + ContentReader reader = this.contentService.getReader(modelNodeRef, + ContentModel.TYPE_CONTENT); + + if (reader.exists() == false) {throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_CUSTOM_MODEL_NO_CONTENT, modelNodeRef.toString()));} + + InputStream contentIn = null; + M2Model deserializedModel = null; + try + { + contentIn = reader.getContentInputStream(); + deserializedModel = M2Model.createModel(contentIn); + } + finally + { + try + { + if (contentIn != null) contentIn.close(); + } + catch (IOException ignored) + { + // Intentionally empty.` + } + } + return deserializedModel; + } + + private void writeCustomContentModel(NodeRef modelRef, M2Model deserializedModel) + { + ContentWriter writer = this.contentService.getWriter(modelRef, ContentModel.TYPE_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_XML); + writer.setEncoding("UTF-8"); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + deserializedModel.toXML(baos); + + String updatedModelXml; + try + { + updatedModelXml = baos.toString("UTF-8"); + writer.putContent(updatedModelXml); + // putContent closes all resources. + // so we don't have to. + } catch (UnsupportedEncodingException uex) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_ERROR_WRITE_CUSTOM_MODEL, modelRef.toString()), uex); + } + } + + + public QName getQNameForClientId(String localName) + { + //TODO 1. After certification. This implementation currently does not support reference, + // property, constraints definitions with the same names, which is technically allowed by Alfresco. + + //TODO 2. Note the implicit assumption here that all custom references will have + // unique titles. This is, in fact, not guaranteed. + + QName propertyResult = null; + for (QName qn : getCustomPropertyDefinitions().keySet()) + { + if (localName != null && localName.equals(qn.getLocalName())) + { + propertyResult = qn; + } + } + + if (propertyResult != null) + { + return propertyResult; + } + + QName referenceResult = null; + for (QName refQn : getCustomReferenceDefinitions().keySet()) + { + if (localName != null && localName.equals(refQn.getLocalName())) + { + referenceResult = refQn; + } + } + + // TODO Handle the case where both are not null + return referenceResult; + } + + private QName generateQNameFor(String clientId) + { + if (getQNameForClientId(clientId) != null) + { + // TODO log it's already taken. What to do? + throw new IllegalArgumentException(I18NUtil.getMessage(MSG_ERROR_CLIENT_ID, clientId)); + } + + String newGUID = GUID.generate(); + QName newQName = QName.createQName(RM_CUSTOM_PREFIX, newGUID, namespaceService); + + return newQName; + } + + public String[] splitSourceTargetId(String sourceTargetId) + { + if (!sourceTargetId.contains(SOURCE_TARGET_ID_SEPARATOR)) + { + throw new IllegalArgumentException(I18NUtil.getMessage(MSG_ERROR_SPLIT_ID, sourceTargetId, SOURCE_TARGET_ID_SEPARATOR)); + } + return sourceTargetId.split(SOURCE_TARGET_ID_SEPARATOR); + } + + public String getCompoundIdFor(String sourceId, String targetId) + { + ParameterCheck.mandatoryString("sourceId", sourceId); + ParameterCheck.mandatoryString("targetId", targetId); + + if (sourceId.contains(SOURCE_TARGET_ID_SEPARATOR)) + { + throw new IllegalArgumentException("sourceId cannot contain '" + SOURCE_TARGET_ID_SEPARATOR + + "': " + sourceId); + } + StringBuilder result = new StringBuilder(); + result.append(sourceId) + .append(SOURCE_TARGET_ID_SEPARATOR) + .append(targetId); + return result.toString(); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementBootstrap.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementBootstrap.java new file mode 100644 index 0000000000..1989960c7d --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementBootstrap.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm; + +import org.alfresco.module.org_alfresco_module_rm.caveat.RMCaveatConfigService; +import org.alfresco.module.org_alfresco_module_rm.email.CustomEmailMappingService; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.transaction.TransactionService; +import org.springframework.context.ApplicationEvent; +import org.springframework.extensions.surf.util.AbstractLifecycleBean; + + +/** + * RM module bootstrap + * + * @author janv + */ +public class RecordsManagementBootstrap extends AbstractLifecycleBean +{ + private TransactionService transactionService; + private RMCaveatConfigService caveatConfigService; + private CustomEmailMappingService customEmailMappingService; + private RecordsManagementAdminService adminService; + + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } + + public void setCaveatConfigService(RMCaveatConfigService caveatConfigService) + { + this.caveatConfigService = caveatConfigService; + } + + public void setCustomEmailMappingService(CustomEmailMappingService customEmailMappingService) + { + this.customEmailMappingService = customEmailMappingService; + } + + public void setRecordsManagementAdminService(RecordsManagementAdminService adminService) + { + this.adminService = adminService; + } + + public CustomEmailMappingService getCustomEmailMappingService() + { + return customEmailMappingService; + } + + @Override + protected void onBootstrap(ApplicationEvent event) + { + // run as System on bootstrap + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() + { + RetryingTransactionCallback callback = new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + // initialise caveat config + caveatConfigService.init(); + + // initialise custom email mapping + customEmailMappingService.init(); + + // Initialise the custom model + adminService.initialiseCustomModel(); + + return null; + } + }; + transactionService.getRetryingTransactionHelper().doInTransaction(callback); + + return null; + } + }, AuthenticationUtil.getSystemUserName()); + } + + @Override + protected void onShutdown(ApplicationEvent event) + { + // NOOP + } +} + diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementPolicies.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementPolicies.java new file mode 100644 index 0000000000..972170d384 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementPolicies.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm; + +import java.io.Serializable; +import java.util.Map; + +import org.alfresco.repo.policy.ClassPolicy; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * Interface containing records management policies + * + * @author Roy Wetherall + */ +public interface RecordsManagementPolicies +{ + /** Policy names */ + public static final QName BEFORE_RM_ACTION_EXECUTION = QName.createQName(NamespaceService.ALFRESCO_URI, "beforeRMActionExecution"); + public static final QName ON_RM_ACTION_EXECUTION = QName.createQName(NamespaceService.ALFRESCO_URI, "onRMActionExecution"); + public static final QName BEFORE_CREATE_REFERENCE = QName.createQName(NamespaceService.ALFRESCO_URI, "beforeCreateReference"); + public static final QName ON_CREATE_REFERENCE = QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateReference"); + public static final QName BEFORE_REMOVE_REFERENCE = QName.createQName(NamespaceService.ALFRESCO_URI, "beforeRemoveReference"); + public static final QName ON_REMOVE_REFERENCE = QName.createQName(NamespaceService.ALFRESCO_URI, "onRemoveReference"); + + /** Before records management action execution */ + public interface BeforeRMActionExecution extends ClassPolicy + { + public void beforeRMActionExecution(NodeRef nodeRef, String name, Map parameters); + } + + /** On records management action execution */ + public interface OnRMActionExecution extends ClassPolicy + { + public void onRMActionExecution(NodeRef nodeRef, String name, Map parameters); + } + + /** Before creation of reference */ + public interface BeforeCreateReference extends ClassPolicy + { + public void beforeCreateReference(NodeRef fromNodeRef, NodeRef toNodeRef, QName reference); + } + + /** On creation of reference */ + public interface OnCreateReference extends ClassPolicy + { + public void onCreateReference(NodeRef fromNodeRef, NodeRef toNodeRef, QName reference); + } + + /** Before removal of reference */ + public interface BeforeRemoveReference extends ClassPolicy + { + public void beforeRemoveReference(NodeRef fromNodeRef, NodeRef toNodeRef, QName reference); + } + + /** On removal of reference */ + public interface OnRemoveReference extends ClassPolicy + { + public void onRemoveReference(NodeRef fromNodeRef, NodeRef toNodeRef, QName reference); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementPoliciesUtil.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementPoliciesUtil.java new file mode 100644 index 0000000000..b281008a86 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementPoliciesUtil.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; + +/** + * @author Roy Wetherall + */ +public class RecordsManagementPoliciesUtil +{ + /** + * Get all aspect and node type qualified names + * + * @param nodeRef + * the node we are interested in + * @return Returns a set of qualified names containing the node type and all + * the node aspects, or null if the node no longer exists + */ + public static Set getTypeAndAspectQNames(final NodeService nodeService, final NodeRef nodeRef) + { + return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork>() + { + public Set doWork() throws Exception + { + Set qnames = null; + try + { + Set aspectQNames = nodeService.getAspects(nodeRef); + + QName typeQName = nodeService.getType(nodeRef); + + qnames = new HashSet(aspectQNames.size() + 1); + qnames.addAll(aspectQNames); + qnames.add(typeQName); + } + catch (InvalidNodeRefException e) + { + qnames = Collections.emptySet(); + } + // done + return qnames; + } + }, AuthenticationUtil.getAdminUserName()); + } + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementService.java new file mode 100644 index 0000000000..76a0f62af9 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementService.java @@ -0,0 +1,462 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * Records management service interface. + * + * Allows simple creation, manipulation and querying of records management components. + * + * @author Roy Wetherall + */ +public interface RecordsManagementService +{ + /********** RM Component methods **********/ + + /** + * Indicates whether the given node is a file plan component or not. + * + * @param nodeRef node reference + * @return boolean true if a file plan component, false otherwise + */ + boolean isFilePlanComponent(NodeRef nodeRef); + + /** + * Returns the 'kind' of file plan component the node reference is. + * Returns null if the given node reference is not a + * file plan component. + * + * @param nodeRef node reference + * @return FilePlanComponentKind the kind of file plan component the + * node is + * kind + * + * @since 2.0 + */ + FilePlanComponentKind getFilePlanComponentKind(NodeRef nodeRef); + + /** + * Indicates whether the given node is file plan node or not. + * + * @param nodeRef node reference + * @return boolean true if node is a file plan node + */ + boolean isFilePlan(NodeRef nodeRef); + + /** + * Indicates whether the given node is a record category or not. + * + * @param nodeRef node reference + * @return boolean true if records category, false otherwise + */ + boolean isRecordCategory(NodeRef nodeRef); + + /** + * Indicates whether the given node is a record folder or not. + * + * @param nodeRef node reference + * @return boolean true if record folder, false otherwise + */ + boolean isRecordFolder(NodeRef nodeRef); + + /** + * Indicates whether the given node is a record or not. + * + * @param nodeRef node reference + * @return boolean true if record, false otherwise + */ + boolean isRecord(NodeRef nodeRef); + + /** + * Indicates whether the given node is a hold (container) or not. + * + * @param nodeRef node reference + * @return boolean true if hold, false otherwise + * + * @since 2.0 + */ + boolean isHold(NodeRef nodeRef); + + /** + * Indicates whether the given node is a transfer (container) or not. + * + * @param nodeRef node reference + * @return boolean true if transfer, false otherwise + * + * @since 2.0 + */ + boolean isTransfer(NodeRef nodeRef); + + /** + * Indicates whether the given node (record or record folder) is a metadata stub or not. + * + * @param nodeRef node reference + * @return boolean true if a metadata stub, false otherwise + * + * @since + */ + boolean isMetadataStub(NodeRef nodeRef); + + /** + * Indicates whether the item is frozen or not. + * + * @param nodeRef node reference + * @return boolean true if record is frozen, false otherwise + * + * @since 2.0 + */ + boolean isFrozen(NodeRef nodeRef); + + + /** + * Indicates whether the item has frozen children or not. + * + * NOTE: this only checks the immediate children and does not check the frozen + * state of the node being passed + * + * @param nodeRef node reference + * @return boolean true if record folder has frozen children, false otherwise + * + * @since 2.0 + */ + boolean hasFrozenChildren(NodeRef nodeRef); + + /** + * Indicates whether the item is cutoff or not. + * + * @param nodeRef node reference + * @return boolean true if the item is cutoff, false otherwise + * + * @since 2.0 + */ + boolean isCutoff(NodeRef nodeRef); + + /** + * Gets the NodeRef sequence from the {@link #getFilePlan(NodeRef) root} + * down to the fileplan component given. The array will start with the NodeRef of the root + * and end with the name of the fileplan component node given. + * + * @param nodeRef a fileplan component + * @return Returns a NodeRef path starting with the name of the + * records management root + */ + List getNodeRefPath(NodeRef nodeRef); + + /** + * Gets the file plan the node is in. + * + * @return {@link NodeRef} file node reference, null if none + */ + NodeRef getFilePlan(NodeRef nodeRef); + + /********** File Plan Methods **********/ + + /** + * Gets all the file plan nodes. + * Searches the SpacesStore by default. + * + * @return List list of file plan nodes + */ + List getFilePlans(); + +// /** +// * Specify the store which should be searched. +// * +// * @see RecordsManagementService#getFilePlans() +// * +// * @param storeRef store reference +// * @return List list of record management root nodes +// */ +// @Deprecated +// List getRecordsManagementRoots(StoreRef storeRef); + + // TODO NodeRef getFilePlanById(String id); + + /** + * Creates a file plan as a child of the given parent node, with the name + * provided. + * + * @param parent parent node reference + * @param name name of the root + * @param type type of root created (must be sub-type of rm:filePlan) + * @return NodeRef node reference to the newly create RM root + */ + NodeRef createFilePlan(NodeRef parent, String name, QName type); + + /** + * @see #createFilePlan(NodeRef, String, QName) + * + * @param parent + * @param name + * @param type + * @param properties + * @return + */ + NodeRef createFilePlan(NodeRef parent, String name, QName type, Map properties); + + /** + * Creates a file plan with the default type. + * + * @see RecordsManagementService#createFilePlan(NodeRef, String, QName) + */ + NodeRef createFilePlan(NodeRef parent, String name); + + /** + * + * @param parent + * @param name + * @param properties + * @return + */ + NodeRef createFilePlan(NodeRef parent, String name, Map properties); + + // TODO void deleteRecordsManagementRoot(NodeRef root); + + /********** Record Category Methods **********/ + + // TODO NodeRef getRecordCategoryByPath(String path); + + // TODO NodeRef getRecordCategoryById(String id); + + // TODO NodeRef getRecordCategoryByName(NodeRef parent, String id); ?? + + /** + * Get all the items contained within a container. This will include record folders and other record categories. + * + * @param recordCategory record category node reference + * @param deep if true then return all children including sub-categories and their children in turn, if false then just + * return the immediate children + * @return {@link List}<{@link NodeRef>} list of contained node references + */ + List getAllContained(NodeRef recordCategory, boolean deep); + + /** + * Only return the immediate children. + * + * @see RecordsManagementService#getAllContained(NodeRef, boolean) + * + * @param recordCategory record category node reference + * @return {@link List}<{@link NodeRef>} list of contained node references + */ + List getAllContained(NodeRef recordCategory); + + /** + * Get all the record categories within a record category. + * + * @param recordCategory record category node reference + * @param deep if true then return all children including sub-categories and their children in turn, if false then just + * return the immediate children + * @return {@link List}<{@link NodeRef>} list of container node references + */ + List getContainedRecordCategories(NodeRef recordCategory, boolean deep); + + /** + * Only return immediate children. + * + * @see RecordsManagementService#getContainedRecordCategories(NodeRef, boolean) + * + * @param recordCategory container node reference + * @return {@link List}<{@link NodeRef>} list of container node references + */ + List getContainedRecordCategories(NodeRef recordCategory); + + /** + * Get all the record folders contained within a container + * + * @param container container node reference + * @param deep if true then return all children including sub-containers and their children in turn, if false then just + * return the immediate children + * @return {@link List}<{@link NodeRef>} list of record folder node references + */ + List getContainedRecordFolders(NodeRef container, boolean deep); + + /** + * Only return immediate children. + * + * @see RecordsManagementService#getContainedRecordFolders(NodeRef, boolean) + * + * @param container container node reference + * @return {@link List}<{@link NodeRef>} list of record folder node references + */ + List getContainedRecordFolders(NodeRef container); + + // TODO List getParentRecordCategories(NodeRef container); // also applicable to record folders + + /** + * Create a record category. + * + * @param parent parent node reference, must be a record category or file plan. + * @param name name of the new record category + * @param type type of container to create, must be a sub-type of rm:recordCategory + * @return NodeRef node reference of the created record category + */ + NodeRef createRecordCategory(NodeRef parent, String name, QName type); + + /** + * + * @param parent + * @param name + * @param type + * @param properties + * @return + */ + NodeRef createRecordCategory(NodeRef parent, String name, QName type, Map properties); + + /** + * Creates a record category of type rma:recordCategory + * + * @see RecordsManagementService#createRecordCategory(NodeRef, String, QName) + * + * @param parent parent node reference, must be a record category or file plan. + * @param name name of the record category + * @return NodeRef node reference of the created record category + */ + NodeRef createRecordCategory(NodeRef parent, String name); + + /** + * + * @param parent + * @param name + * @param properties + * @return + */ + NodeRef createRecordCategory(NodeRef parent, String name, Map properties); + + // TODO void deleteRecordCategory(NodeRef container); + + // TODO move, copy, link ?? + + /********** Record Folder methods **********/ + + /** + * Indicates whether the contents of a record folder are all declared. + * + * @param nodeRef node reference (record folder) + * @return boolean true if record folder contents are declared, false otherwise + */ + boolean isRecordFolderDeclared(NodeRef nodeRef); + + /** + * Indicates whether a record folder is closed or not. + * + * @param nodeRef node reference (record folder) + * @return boolean true if record folder is closed, false otherwise + * + * @since 2.0 + */ + boolean isRecordFolderClosed(NodeRef nodeRef); + + // TODO NodeRef getRecordFolderByPath(String path); + + // TODO NodeRef getRecordFolderById(String id); + + // TODO NodeRef getRecordFolderByName(NodeRef parent, String name); + + + /** + * Create a record folder in the rm container. The record folder with take the name and type + * provided. + * + * @param rmContainer records management container + * @param name name + * @param type type + * @return NodeRef node reference of record folder + */ + NodeRef createRecordFolder(NodeRef rmContainer, String name, QName type); + + /** + * + * @param rmContainer + * @param name + * @param type + * @param properties + * @return + */ + NodeRef createRecordFolder(NodeRef rmContainer, String name, QName type, Map properties); + + /** + * Type defaults to rm:recordFolder + * + * @see RecordsManagementService#createRecordCategory(NodeRef, String, QName) + * + * @param rmContainer records management container + * @param name name + * @return NodeRef node reference of record folder + */ + NodeRef createRecordFolder(NodeRef parent, String name); + + /** + * + * @param parent + * @param name + * @param properties + * @return + */ + NodeRef createRecordFolder(NodeRef parent, String name, Map properties); + + // TODO void deleteRecordFolder(NodeRef recordFolder); + + // TODO List getParentRecordsManagementContainers(NodeRef container); // also applicable to record folders + + /** + * Gets a list of all the records within a record folder + * + * @param recordFolder record folder + * @return List list of records in the record folder + */ + // TODO rename to getContainedRecords(NodeRef recordFolder); + List getRecords(NodeRef recordFolder); + + // TODO move? copy? link? + + /********** Record methods **********/ + + /** + * Get a list of all the record meta-data aspects + * + * @return {@link Set}<{@link QName}> list of record meta-data aspects + */ + Set getRecordMetaDataAspects(); + + /** + * Get all the record folders that a record is filed into. + * + * @param record the record node reference + * @return List list of folder record node references + */ + // TODO rename to List getParentRecordFolders(NodeRef record); + List getRecordFolders(NodeRef record); + + /** + * Indicates whether the record is declared + * + * @param nodeRef node reference (record) + * @return boolean true if record is declared, false otherwise + */ + boolean isRecordDeclared(NodeRef nodeRef); +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceImpl.java new file mode 100644 index 0000000000..520cebc015 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceImpl.java @@ -0,0 +1,1179 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementCustomModel; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.policy.Behaviour.NotificationFrequency; +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.util.Pair; +import org.alfresco.util.ParameterCheck; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * Records management service implementation. + * + * @author Roy Wetherall + */ +public class RecordsManagementServiceImpl implements RecordsManagementService, + RecordsManagementModel, + RecordsManagementPolicies.OnCreateReference, + RecordsManagementPolicies.OnRemoveReference +{ + /** I18N */ + private final static String MSG_ERROR_ADD_CONTENT_CONTAINER = "rm.service.error-add-content-container"; + private final static String MSG_UPDATE_DISP_ACT_DEF = "rm.service.update-disposition-action-def"; + private final static String MSG_SET_ID = "rm.service.set-id"; + private final static String MSG_PATH_NODE = "rm.service.path-node"; + private final static String MSG_INVALID_RM_NODE = "rm.service.invalid-rm-node"; + private final static String MSG_NO_ROOT = "rm.service.no-root"; + private final static String MSG_DUP_ROOT = "rm.service.dup-root"; + private final static String MSG_ROOT_TYPE = "rm.service.root-type"; + private final static String MSG_CONTAINER_PARENT_TYPE= "rm.service.container-parent-type"; + private final static String MSG_CONTAINER_TYPE = "rm.service.container-type"; + private final static String MSG_CONTAINER_EXPECTED = "rm.service.container-expected"; + private final static String MSG_RECORD_FOLDER_EXPECTED = "rm.service.record-folder-expected"; + private final static String MSG_PARENT_RECORD_FOLDER_ROOT = "rm.service.parent-record-folder-root"; + private final static String MSG_PARENT_RECORD_FOLDER_TYPE = "rm.service.parent-record-folder-type"; + private final static String MSG_RECORD_FOLDER_TYPE = "rm.service.record-folder-type"; + private final static String MSG_NOT_RECORD = "rm.service.not-record"; + + /** Store that the RM roots are contained within */ + @SuppressWarnings("unused") + @Deprecated + private StoreRef defaultStoreRef = StoreRef.STORE_REF_WORKSPACE_SPACESSTORE; + + /** Service registry */ + private RecordsManagementServiceRegistry serviceRegistry; + + /** Dictionary service */ + private DictionaryService dictionaryService; + + /** Node service */ + private NodeService nodeService; + + /** Node DAO */ + private NodeDAO nodeDAO; + + /** Policy component */ + private PolicyComponent policyComponent; + + /** Records management action service */ + private RecordsManagementActionService rmActionService; + + /** Well-known location of the scripts folder. */ + private NodeRef scriptsFolderNodeRef = new NodeRef("workspace", "SpacesStore", "rm_scripts"); + + /** List of available record meta-data aspects */ + private Set recordMetaDataAspects; + + /** Java behaviour */ + private JavaBehaviour onChangeToDispositionActionDefinition; + + /** + * Set the service registry service + * + * @param serviceRegistry service registry + */ + public void setRecordsManagementServiceRegistry(RecordsManagementServiceRegistry serviceRegistry) + { + // Internal ops use the unprotected services from the voter (e.g. nodeService) + this.serviceRegistry = serviceRegistry; + this.dictionaryService = serviceRegistry.getDictionaryService(); + } + + /** + * Set policy component + * + * @param policyComponent policy component + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * Set search service + * + * @param nodeService search service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Set the node DAO object + * + * @param nodeDAO node DAO + */ + public void setNodeDAO(NodeDAO nodeDAO) + { + this.nodeDAO = nodeDAO; + } + + /** + * Set records management action service + * + * @param rmActionService records management action service + */ + public void setRmActionService(RecordsManagementActionService rmActionService) + { + this.rmActionService = rmActionService; + } + + /** + * Sets the default RM store reference + * @param defaultStoreRef store reference + */ + @Deprecated + public void setDefaultStoreRef(StoreRef defaultStoreRef) + { + this.defaultStoreRef = defaultStoreRef; + } + + /** + * Init method. Registered behaviours. + */ + public void init() + { + // Register the association behaviours + this.policyComponent.bindAssociationBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateChildAssociation"), + TYPE_RECORD_FOLDER, + ContentModel.ASSOC_CONTAINS, + new JavaBehaviour(this, "onFileContent", NotificationFrequency.TRANSACTION_COMMIT)); + + this.policyComponent.bindAssociationBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateChildAssociation"), + TYPE_RECORD_CATEGORY, + ContentModel.ASSOC_CONTAINS, + new JavaBehaviour(this, "onAddContentToContainer", NotificationFrequency.EVERY_EVENT)); + + // Register script execution behaviour on RM property update. + this.policyComponent.bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"), + ASPECT_FILE_PLAN_COMPONENT, + new JavaBehaviour(this, "onChangeToAnyRmProperty", NotificationFrequency.TRANSACTION_COMMIT)); + + // Disposition behaviours + onChangeToDispositionActionDefinition = new JavaBehaviour(this, "onChangeToDispositionActionDefinition", NotificationFrequency.TRANSACTION_COMMIT); + this.policyComponent.bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"), + TYPE_DISPOSITION_ACTION_DEFINITION, + onChangeToDispositionActionDefinition); + + // Reference behaviours + policyComponent.bindClassBehaviour(RecordsManagementPolicies.ON_CREATE_REFERENCE, + ASPECT_RECORD, + new JavaBehaviour(this, "onCreateReference", NotificationFrequency.TRANSACTION_COMMIT)); + + policyComponent.bindClassBehaviour(RecordsManagementPolicies.ON_REMOVE_REFERENCE, + ASPECT_RECORD, + new JavaBehaviour(this, "onRemoveReference", NotificationFrequency.TRANSACTION_COMMIT)); + + // Identifier behaviours + policyComponent.bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"), + ASPECT_RECORD_COMPONENT_ID, + new JavaBehaviour(this, "onIdentifierUpdate", NotificationFrequency.TRANSACTION_COMMIT)); + } + + /** + * Try to file any record created in a record folder + * + * @see org.alfresco.repo.node.NodeServicePolicies.OnCreateChildAssociationPolicy#onCreateChildAssociation(org.alfresco.service.cmr.repository.ChildAssociationRef, boolean) + */ + public void onFileContent(ChildAssociationRef childAssocRef, boolean bNew) + { + // File the document + rmActionService.executeRecordsManagementAction(childAssocRef.getChildRef(), "file"); + } + + /** + * On add content to container + * + * Prevents content nodes being added to record series and record category folders + * by imap, cifs etc. + * + * @param childAssocRef + * @param bNew + */ + public void onAddContentToContainer(ChildAssociationRef childAssocRef, boolean bNew) + { + if (childAssocRef.getTypeQName().equals(ContentModel.ASSOC_CONTAINS)) + { + QName childType = nodeService.getType(childAssocRef.getChildRef()); + + if(childType.equals(ContentModel.TYPE_CONTENT)) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_ERROR_ADD_CONTENT_CONTAINER)); + } + } + } + + /** + * Called after a DispositionActionDefinition property has been updated. + */ + public void onChangeToDispositionActionDefinition(NodeRef node, Map oldProps, Map newProps) + { + if (nodeService.exists(node) == true) + { + onChangeToDispositionActionDefinition.disable(); + try + { + // Determine the properties that have changed + Set changedProps = this.determineChangedProps(oldProps, newProps); + + if (nodeService.hasAspect(node, ASPECT_UNPUBLISHED_UPDATE) == false) + { + // Apply the unpublished aspect + Map props = new HashMap(); + props.put(PROP_UPDATE_TO, UPDATE_TO_DISPOSITION_ACTION_DEFINITION); + props.put(PROP_UPDATED_PROPERTIES, (Serializable)changedProps); + nodeService.addAspect(node, ASPECT_UNPUBLISHED_UPDATE, props); + } + else + { + Map props = nodeService.getProperties(node); + + // Check that there isn't a update currently being published + if ((Boolean)props.get(PROP_PUBLISH_IN_PROGRESS).equals(Boolean.TRUE) == true) + { + // Can not update the disposition schedule since there is an outstanding update being published + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_UPDATE_DISP_ACT_DEF)); + } + + // Update the update information + props.put(PROP_UPDATE_TO, UPDATE_TO_DISPOSITION_ACTION_DEFINITION); + props.put(PROP_UPDATED_PROPERTIES, (Serializable)changedProps); + nodeService.setProperties(node, props); + } + } + finally + { + onChangeToDispositionActionDefinition.enable(); + } + } + } + + /** + * Called after any Records Management property has been updated. + */ + public void onChangeToAnyRmProperty(NodeRef node, Map oldProps, Map newProps) + { + if (nodeService.exists(node) == true) + { + this.lookupAndExecuteScripts(node, oldProps, newProps); + } + } + + /** + * Property update behaviour implementation + * + * @param node + * @param oldProps + * @param newProps + */ + public void onIdentifierUpdate(NodeRef node, Map oldProps, Map newProps) + { + if (nodeService.exists(node) == true) + { + String newIdValue = (String)newProps.get(PROP_IDENTIFIER); + if (newIdValue != null) + { + String oldIdValue = (String)oldProps.get(PROP_IDENTIFIER); + if (oldIdValue != null && oldIdValue.equals(newIdValue) == false) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_SET_ID, node.toString())); + } + } + } + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.OnCreateReference#onCreateReference(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void onCreateReference(NodeRef fromNodeRef, NodeRef toNodeRef, QName reference) + { + // Deal with versioned records + if (reference.equals(QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_URI, "versions")) == true) + { + // Apply the versioned aspect to the from node + this.nodeService.addAspect(fromNodeRef, ASPECT_VERSIONED_RECORD, null); + } + + // Execute script if for the reference event + executeReferenceScript("onCreate", reference, fromNodeRef, toNodeRef); + } + + /** + * Executes a reference script if present + * + * @param policy + * @param reference + * @param from + * @param to + */ + private void executeReferenceScript(String policy, QName reference, NodeRef from, NodeRef to) + { + String referenceId = reference.getLocalName(); + + // This is the filename pattern which is assumed. + // e.g. a script file onCreate_superceded.js for the creation of a superseded reference + String expectedScriptName = policy + "_" + referenceId + ".js"; + + NodeRef scriptNodeRef = nodeService.getChildByName(scriptsFolderNodeRef, ContentModel.ASSOC_CONTAINS, expectedScriptName); + if (scriptNodeRef != null) + { + Map objectModel = new HashMap(1); + objectModel.put("node", from); + objectModel.put("toNode", to); + objectModel.put("policy", policy); + objectModel.put("reference", referenceId); + + serviceRegistry.getScriptService().executeScript(scriptNodeRef, null, objectModel); + } + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.OnRemoveReference#onRemoveReference(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void onRemoveReference(NodeRef fromNodeRef, NodeRef toNodeRef, QName reference) + { + // Deal with versioned records + if (reference.equals(QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_URI, "versions")) == true) + { + // Apply the versioned aspect to the from node + this.nodeService.removeAspect(fromNodeRef, ASPECT_VERSIONED_RECORD); + } + + // Execute script if for the reference event + executeReferenceScript("onRemove", reference, fromNodeRef, toNodeRef); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#isFilePlan(org.alfresco.service.cmr.repository.NodeRef) + */ + public boolean isFilePlan(NodeRef nodeRef) + { + return instanceOf(nodeRef, TYPE_FILE_PLAN); + } + + /** + * Utility method to safely and quickly determine if a node is a type (or sub-type) of the one specified. + */ + private boolean instanceOf(NodeRef nodeRef, QName ofClassName) + { + ParameterCheck.mandatory("nodeRef", nodeRef); + ParameterCheck.mandatory("ofClassName", ofClassName); + boolean result = false; + if (nodeService.exists(nodeRef) == true && + (ofClassName.equals(nodeService.getType(nodeRef)) == true || + dictionaryService.isSubClass(nodeService.getType(nodeRef), ofClassName) == true)) + { + result = true; + } + return result; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#isFilePlanComponent(org.alfresco.service.cmr.repository.NodeRef) + */ + public boolean isFilePlanComponent(NodeRef nodeRef) + { + boolean result = false; + if (nodeService.exists(nodeRef) == true && + nodeService.hasAspect(nodeRef, ASPECT_FILE_PLAN_COMPONENT) == true) + { + result = true; + } + return result; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#getFilePlanComponentKind(org.alfresco.service.cmr.repository.NodeRef) + */ + public FilePlanComponentKind getFilePlanComponentKind(NodeRef nodeRef) + { + FilePlanComponentKind result = null; + + if (isFilePlanComponent(nodeRef) == true) + { + result = FilePlanComponentKind.FILE_PLAN_COMPONENT; + + if (isFilePlan(nodeRef) == true) + { + result = FilePlanComponentKind.FILE_PLAN; + } + else if (isRecordCategory(nodeRef) == true) + { + result = FilePlanComponentKind.RECORD_CATEGORY; + } + else if (isRecordFolder(nodeRef) == true) + { + result = FilePlanComponentKind.RECORD_FOLDER; + } + else if (isRecord(nodeRef) == true) + { + result = FilePlanComponentKind.RECORD; + } + else if (isHold(nodeRef) == true) + { + result = FilePlanComponentKind.HOLD; + } + else if (isTransfer(nodeRef) == true) + { + result = FilePlanComponentKind.TRANSFER; + } + else if (instanceOf(nodeRef, TYPE_DISPOSITION_SCHEDULE) == true || instanceOf(nodeRef, TYPE_DISPOSITION_ACTION_DEFINITION) == true) + { + result = FilePlanComponentKind.DISPOSITION_SCHEDULE; + } + } + + return result; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#isRecordCategory(org.alfresco.service.cmr.repository.NodeRef) + */ + public boolean isRecordCategory(NodeRef nodeRef) + { + return instanceOf(nodeRef, TYPE_RECORD_CATEGORY); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#isRecordFolder(org.alfresco.service.cmr.repository.NodeRef) + */ + public boolean isRecordFolder(NodeRef nodeRef) + { + return instanceOf(nodeRef, TYPE_RECORD_FOLDER); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#isRecord(org.alfresco.service.cmr.repository.NodeRef) + */ + public boolean isRecord(NodeRef nodeRef) + { + return this.nodeService.hasAspect(nodeRef, ASPECT_RECORD); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#isHold(org.alfresco.service.cmr.repository.NodeRef) + */ + public boolean isHold(NodeRef nodeRef) + { + return instanceOf(nodeRef, TYPE_HOLD); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#isTransfer(org.alfresco.service.cmr.repository.NodeRef) + */ + public boolean isTransfer(NodeRef nodeRef) + { + return instanceOf(nodeRef, TYPE_TRANSFER); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#isMetadataStub(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public boolean isMetadataStub(NodeRef nodeRef) + { + return nodeService.hasAspect(nodeRef, ASPECT_GHOSTED); + } + + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#isFrozen(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public boolean isFrozen(NodeRef nodeRef) + { + return nodeService.hasAspect(nodeRef, ASPECT_FROZEN); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#hasFrozenChildren(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public boolean hasFrozenChildren(NodeRef nodeRef) + { + boolean result = false; + if (isFilePlanComponent(nodeRef) == true) + { + List assocs = nodeService.getChildAssocs(nodeRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef assoc : assocs) + { + if (isFrozen(assoc.getChildRef()) == true) + { + result = true; + break; + } + } + } + return result; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#isCutoff(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public boolean isCutoff(NodeRef nodeRef) + { + return nodeService.hasAspect(nodeRef, ASPECT_CUT_OFF); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#getFilePlan(org.alfresco.service.cmr.repository.NodeRef) + */ + public NodeRef getFilePlan(NodeRef nodeRef) + { + NodeRef result = null; + + if (nodeRef != null) + { + result = (NodeRef)nodeService.getProperty(nodeRef, PROP_ROOT_NODEREF); + if (result == null) + { + if (instanceOf(nodeRef, TYPE_FILE_PLAN) == true) + { + result = nodeRef; + } + else + { + ChildAssociationRef parentAssocRef = nodeService.getPrimaryParent(nodeRef); + if (parentAssocRef != null) + { + result = getFilePlan(parentAssocRef.getParentRef()); + } + } + } + } + + return result; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#getNodeRefPath(org.alfresco.service.cmr.repository.NodeRef) + */ + public List getNodeRefPath(NodeRef nodeRef) + { + LinkedList nodeRefPath = new LinkedList(); + try + { + getNodeRefPathRecursive(nodeRef, nodeRefPath); + } + catch (Throwable e) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_PATH_NODE, nodeRef), e); + } + return nodeRefPath; + } + + /** + * Helper method to build a NodeRef path from the node to the RM root + */ + private void getNodeRefPathRecursive(NodeRef nodeRef, LinkedList nodeRefPath) + { + if (isFilePlanComponent(nodeRef) == false) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_INVALID_RM_NODE, ASPECT_FILE_PLAN_COMPONENT.toString())); + } + // Prepend it to the path + nodeRefPath.addFirst(nodeRef); + // Are we at the root + if (isFilePlan(nodeRef) == true) + { + // We're done + } + else + { + ChildAssociationRef assocRef = nodeService.getPrimaryParent(nodeRef); + if (assocRef == null) + { + // We hit the top of the store + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NO_ROOT)); + } + // Recurse + nodeRef = assocRef.getParentRef(); + getNodeRefPathRecursive(nodeRef, nodeRefPath); + } + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#getRecordsManagementRoots(org.alfresco.service.cmr.repository.StoreRef) + */ + public List getFilePlans() + { + final List results = new ArrayList(); + Set aspects = new HashSet(1); + aspects.add(ASPECT_RECORDS_MANAGEMENT_ROOT); + nodeDAO.getNodesWithAspects(aspects, Long.MIN_VALUE, Long.MAX_VALUE, new NodeDAO.NodeRefQueryCallback() + { + @Override + public boolean handle(Pair nodePair) + { + results.add(nodePair.getSecond()); + return true; + } + }); + return results; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#createFilePlan(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, org.alfresco.service.namespace.QName, java.util.Map) + */ + public NodeRef createFilePlan(NodeRef parent, String name, QName type, Map properties) + { + ParameterCheck.mandatory("parent", parent); + ParameterCheck.mandatory("name", name); + ParameterCheck.mandatory("type", type); + + // Check the parent is not already an RM component node + // ie: you can't create a rm root in an existing rm hierarchy + if (isFilePlanComponent(parent) == true) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_DUP_ROOT)); + } + + // Check that the passed type is a sub-type of rma:filePlan + if (TYPE_FILE_PLAN.equals(type) == false && + dictionaryService.isSubClass(type, TYPE_FILE_PLAN) == false) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_ROOT_TYPE, type.toString())); + } + + // Build map of properties + Map rmRootProps = new HashMap(1); + if (properties != null && properties.size() != 0) + { + rmRootProps.putAll(properties); + } + rmRootProps.put(ContentModel.PROP_NAME, name); + + // Create the root + ChildAssociationRef assocRef = nodeService.createNode( + parent, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name), + type, + rmRootProps); + + // TODO do we need to create role and security groups or is this done automatically? + + return assocRef.getChildRef(); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#createFilePlan(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, java.util.Map) + */ + public NodeRef createFilePlan(NodeRef parent, String name, Map properties) + { + return createFilePlan(parent, name, TYPE_FILE_PLAN, properties); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#createFilePlan(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public NodeRef createFilePlan(NodeRef parent, String name) + { + return createFilePlan(parent, name, TYPE_FILE_PLAN, null); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#createFilePlan(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, org.alfresco.service.namespace.QName) + */ + @Override + public NodeRef createFilePlan(NodeRef parent, String name, QName type) + { + return createFilePlan(parent, name, type, null); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#createRecordCategory(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, org.alfresco.service.namespace.QName, java.util.Map) + */ + public NodeRef createRecordCategory(NodeRef parent, String name, QName type, Map properties) + { + ParameterCheck.mandatory("parent", parent); + ParameterCheck.mandatory("name", name); + ParameterCheck.mandatory("type", type); + + // Check that the parent is a container + QName parentType = nodeService.getType(parent); + if (TYPE_RECORDS_MANAGEMENT_CONTAINER.equals(parentType) == false && + dictionaryService.isSubClass(parentType, TYPE_RECORDS_MANAGEMENT_CONTAINER) == false) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_CONTAINER_PARENT_TYPE, parentType.toString())); + } + + // Check that the the provided type is a sub-type of rm:recordCategory + if (TYPE_RECORD_CATEGORY.equals(type) == false && + dictionaryService.isSubClass(type, TYPE_RECORD_CATEGORY) == false) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_CONTAINER_TYPE, type.toString())); + } + + // Set the properties for the record category + Map props = new HashMap(1); + if (properties != null && properties.size() != 0) + { + props.putAll(properties); + } + props.put(ContentModel.PROP_NAME, name); + + return nodeService.createNode( + parent, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name), + type, + props).getChildRef(); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#createRecordCategory(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public NodeRef createRecordCategory(NodeRef parent, String name) + { + return createRecordCategory(parent, name, TYPE_RECORD_CATEGORY); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#createRecordCategory(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, java.util.Map) + */ + public NodeRef createRecordCategory(NodeRef parent, String name, Map properties) + { + return createRecordCategory(parent, name, TYPE_RECORD_CATEGORY, properties); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#createRecordCategory(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, org.alfresco.service.namespace.QName) + */ + public NodeRef createRecordCategory(NodeRef parent, String name, QName type) + { + return createRecordCategory(parent, name, type, null); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#getAllContained(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public List getAllContained(NodeRef container) + { + return getAllContained(container, false); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#getAllContained(org.alfresco.service.cmr.repository.NodeRef, boolean) + */ + @Override + public List getAllContained(NodeRef container, boolean deep) + { + return getContained(container, null, deep); + } + + /** + * Get contained nodes of a particular type. If null return all. + * + * @param container container node reference + * @param typeFilter type filter, null if none + * @return {@link List}<{@link NodeRef> list of contained node references + */ + private List getContained(NodeRef container, QName typeFilter, boolean deep) + { + // Parameter check + ParameterCheck.mandatory("container", container); + + // Check we have a container in our hands + if (isRecordCategory(container) == false) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_CONTAINER_EXPECTED)); + } + + List result = new ArrayList(1); + List assocs = this.nodeService.getChildAssocs(container, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef assoc : assocs) + { + NodeRef child = assoc.getChildRef(); + QName childType = nodeService.getType(child); + if (typeFilter == null || + typeFilter.equals(childType) == true || + dictionaryService.isSubClass(childType, typeFilter) == true) + { + result.add(child); + } + + // Inspect the containers and add children if deep + if (deep == true && + (TYPE_RECORD_CATEGORY.equals(childType) == true || + dictionaryService.isSubClass(childType, TYPE_RECORD_CATEGORY) == true)) + { + result.addAll(getContained(child, typeFilter, deep)); + } + } + + return result; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#getContainedRecordCategories(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public List getContainedRecordCategories(NodeRef container) + { + return getContainedRecordCategories(container, false); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#getContainedRecordCategories(org.alfresco.service.cmr.repository.NodeRef, boolean) + */ + @Override + public List getContainedRecordCategories(NodeRef container, boolean deep) + { + return getContained(container, TYPE_RECORD_CATEGORY, deep); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#getContainedRecordFolders(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public List getContainedRecordFolders(NodeRef container) + { + return getContainedRecordFolders(container, false); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#getContainedRecordFolders(org.alfresco.service.cmr.repository.NodeRef, boolean) + */ + @Override + public List getContainedRecordFolders(NodeRef container, boolean deep) + { + return getContained(container, TYPE_RECORD_FOLDER, deep); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#isRecordFolderDeclared(org.alfresco.service.cmr.repository.NodeRef) + */ + public boolean isRecordFolderDeclared(NodeRef recordFolder) + { + // Check we have a record folder + if (isRecordFolder(recordFolder) == false) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_RECORD_FOLDER_EXPECTED)); + } + + boolean result = true; + + // Check that each record in the record folder in declared + List records = getRecords(recordFolder); + for (NodeRef record : records) + { + if (isRecordDeclared(record) == false) + { + result = false; + break; + } + } + + return result; + + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#isRecordFolderClosed(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public boolean isRecordFolderClosed(NodeRef nodeRef) + { + // Check we have a record folder + if (isRecordFolder(nodeRef) == false) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_RECORD_FOLDER_EXPECTED)); + } + + return ((Boolean)this.nodeService.getProperty(nodeRef, PROP_IS_CLOSED)).booleanValue(); + } + + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#getRecordFolders(org.alfresco.service.cmr.repository.NodeRef) + */ + public List getRecordFolders(NodeRef record) + { + List result = new ArrayList(1); + if (isRecord(record) == true) + { + List assocs = this.nodeService.getParentAssocs(record, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef assoc : assocs) + { + NodeRef parent = assoc.getParentRef(); + if (isRecordFolder(parent) == true) + { + result.add(parent); + } + } + } + return result; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#createRecordFolder(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, org.alfresco.service.namespace.QName, java.util.Map) + */ + public NodeRef createRecordFolder(NodeRef rmContainer, String name, QName type, Map properties) + { + ParameterCheck.mandatory("rmContainer", rmContainer); + ParameterCheck.mandatory("name", name); + ParameterCheck.mandatory("type", type); + + // Check that we are not trying to create a record folder in a root container + if (isFilePlan(rmContainer) == true) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_PARENT_RECORD_FOLDER_ROOT)); + } + + // Check that the parent is a container + QName parentType = nodeService.getType(rmContainer); + if (TYPE_RECORD_CATEGORY.equals(parentType) == false && + dictionaryService.isSubClass(parentType, TYPE_RECORD_CATEGORY) == false) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_PARENT_RECORD_FOLDER_TYPE, parentType.toString())); + } + + // Check that the the provided type is a sub-type of rm:recordFolder + if (TYPE_RECORD_FOLDER.equals(type) == false && + dictionaryService.isSubClass(type, TYPE_RECORD_FOLDER) == false) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_RECORD_FOLDER_TYPE, type.toString())); + } + + Map props = new HashMap(1); + if (properties != null && properties.size() != 0) + { + props.putAll(properties); + } + props.put(ContentModel.PROP_NAME, name); + + return nodeService.createNode( + rmContainer, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name), + type, + props).getChildRef(); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#createRecordFolder(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public NodeRef createRecordFolder(NodeRef rmContrainer, String name) + { + // TODO defaults to rm:recordFolder, but in future could auto-detect sub-type of folder based on + // context + return createRecordFolder(rmContrainer, name, TYPE_RECORD_FOLDER); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#createRecordFolder(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, java.util.Map) + */ + public NodeRef createRecordFolder(NodeRef parent, String name, Map properties) + { + return createRecordFolder(parent, name, TYPE_RECORD_FOLDER, properties); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#createRecordFolder(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, org.alfresco.service.namespace.QName) + */ + public NodeRef createRecordFolder(NodeRef parent, String name, QName type) + { + return createRecordFolder(parent, name, type, null); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#getRecordMetaDataAspects() + */ + public Set getRecordMetaDataAspects() + { + if (recordMetaDataAspects == null) + { + recordMetaDataAspects = new HashSet(7); + Collection aspects = dictionaryService.getAllAspects(); + for (QName aspect : aspects) + { + AspectDefinition def = dictionaryService.getAspect(aspect); + if (def != null) + { + QName parent = def.getParentName(); + if (parent != null && ASPECT_RECORD_META_DATA.equals(parent) == true) + { + recordMetaDataAspects.add(aspect); + } + } + } + } + return recordMetaDataAspects; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#getRecords(org.alfresco.service.cmr.repository.NodeRef) + */ + public List getRecords(NodeRef recordFolder) + { + List result = new ArrayList(1); + if (isRecordFolder(recordFolder) == true) + { + List assocs = this.nodeService.getChildAssocs(recordFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef assoc : assocs) + { + NodeRef child = assoc.getChildRef(); + if (isRecord(child) == true) + { + result.add(child); + } + } + } + return result; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#isRecord(org.alfresco.service.cmr.repository.NodeRef, boolean) + */ + public boolean isRecordDeclared(NodeRef record) + { + if (isRecord(record) == false) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NOT_RECORD, record.toString())); + } + return (this.nodeService.hasAspect(record, ASPECT_DECLARED_RECORD)); + } + + + + /** + * This method examines the old and new property sets and for those properties which + * have changed, looks for script resources corresponding to those properties. + * Those scripts are then called via the ScriptService. + * + * @param nodeWithChangedProperties the node whose properties have changed. + * @param oldProps the old properties and their values. + * @param newProps the new properties and their values. + * + * @see #lookupScripts(Map, Map) + */ + private void lookupAndExecuteScripts(NodeRef nodeWithChangedProperties, + Map oldProps, Map newProps) + { + List scriptRefs = lookupScripts(oldProps, newProps); + + Map objectModel = new HashMap(1); + objectModel.put("node", nodeWithChangedProperties); + objectModel.put("oldProperties", oldProps); + objectModel.put("newProperties", newProps); + + for (NodeRef scriptRef : scriptRefs) + { + serviceRegistry.getScriptService().executeScript(scriptRef, null, objectModel); + } + } + + /** + * This method determines which properties have changed and for each such property + * looks for a script resource in a well-known location. + * + * @param oldProps the old properties and their values. + * @param newProps the new properties and their values. + * @return A list of nodeRefs corresponding to the Script resources. + * + * @see #determineChangedProps(Map, Map) + */ + private List lookupScripts(Map oldProps, Map newProps) + { + List result = new ArrayList(); + + Set changedProps = determineChangedProps(oldProps, newProps); + for (QName propQName : changedProps) + { + QName prefixedQName = propQName.getPrefixedQName(serviceRegistry.getNamespaceService()); + + String [] splitQName = QName.splitPrefixedQName(prefixedQName.toPrefixString()); + final String shortPrefix = splitQName[0]; + final String localName = splitQName[1]; + + // This is the filename pattern which is assumed. + // e.g. a script file cm_name.js would be called for changed to cm:name + String expectedScriptName = shortPrefix + "_" + localName + ".js"; + + NodeRef nextElement = nodeService.getChildByName(scriptsFolderNodeRef, ContentModel.ASSOC_CONTAINS, expectedScriptName); + if (nextElement != null) result.add(nextElement); + } + + return result; + } + + /** + * This method compares the oldProps map against the newProps map and returns + * a set of QNames of the properties that have changed. Changed here means one of + *
    + *
  • the property has been removed
  • + *
  • the property has had its value changed
  • + *
  • the property has been added
  • + *
+ */ + private Set determineChangedProps(Map oldProps, Map newProps) + { + Set result = new HashSet(); + for (QName qn : oldProps.keySet()) + { + if (newProps.get(qn) == null || + newProps.get(qn).equals(oldProps.get(qn)) == false) + { + result.add(qn); + } + } + for (QName qn : newProps.keySet()) + { + if (oldProps.get(qn) == null) + { + result.add(qn); + } + } + + return result; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceRegistry.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceRegistry.java new file mode 100644 index 0000000000..bca1f20bcd --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceRegistry.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm; + +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; +import org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditService; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService; +import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService; +import org.alfresco.service.NotAuditable; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * Records management service registry + * + * @author Roy Wetherall + */ +public interface RecordsManagementServiceRegistry extends ServiceRegistry +{ + /** Service QName constants */ + static final QName RECORDS_MANAGEMENT_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "RecordsManagementService"); + static final QName DISPOSITION_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "DispositionService"); + static final QName RECORDS_MANAGEMENT_ADMIN_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "RecordsManagementAdminService"); + static final QName RECORDS_MANAGEMENT_ACTION_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "RecordsManagementActionService"); + static final QName RECORDS_MANAGEMENT_EVENT_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "RecordsManagementEventService"); + static final QName RECORDS_MANAGEMENT_SECURITY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "RecordsManagementSecurityService"); + static final QName RECORDS_MANAGEMENT_AUDIT_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "RecordsManagementAuditService"); + + /** + * @return records management service + */ + @NotAuditable + RecordsManagementService getRecordsManagementService(); + + /** + * @return disposition service + */ + @NotAuditable + DispositionService getDispositionService(); + + /** + * @return records management admin service + */ + @NotAuditable + RecordsManagementAdminService getRecordsManagementAdminService(); + + /** + * @return records management action service + */ + @NotAuditable + RecordsManagementActionService getRecordsManagementActionService(); + + /** + * @return records management event service + */ + @NotAuditable + RecordsManagementEventService getRecordsManagementEventService(); + + /** + * @return records management security service + */ + @NotAuditable + RecordsManagementSecurityService getRecordsManagementSecurityService(); + + /** + * @return records management audit service + */ + @NotAuditable + RecordsManagementAuditService getRecordsManagementAuditService(); +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceRegistryImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceRegistryImpl.java new file mode 100644 index 0000000000..253fd248a3 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceRegistryImpl.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm; + +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; +import org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditService; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService; +import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService; +import org.alfresco.repo.service.ServiceDescriptorRegistry; + +/** + * Records management service registry implementation + * + * @author Roy Wetherall + */ +public class RecordsManagementServiceRegistryImpl extends ServiceDescriptorRegistry + implements RecordsManagementServiceRegistry +{ + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry#getRecordsManagementActionService() + */ + public RecordsManagementActionService getRecordsManagementActionService() + { + return (RecordsManagementActionService)getService(RECORDS_MANAGEMENT_ACTION_SERVICE); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry#getRecordsManagementAdminService() + */ + public RecordsManagementAdminService getRecordsManagementAdminService() + { + return (RecordsManagementAdminService)getService(RECORDS_MANAGEMENT_ADMIN_SERVICE); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry#getRecordsManagementEventService() + */ + public RecordsManagementEventService getRecordsManagementEventService() + { + return (RecordsManagementEventService)getService(RECORDS_MANAGEMENT_EVENT_SERVICE); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry#getRecordsManagementService() + */ + public RecordsManagementService getRecordsManagementService() + { + return (RecordsManagementService)getService(RECORDS_MANAGEMENT_SERVICE); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry#getRecordsManagementSecurityService() + */ + public RecordsManagementSecurityService getRecordsManagementSecurityService() + { + return (RecordsManagementSecurityService)getService(RECORDS_MANAGEMENT_SECURITY_SERVICE); + } + + /* + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry#getRecordsManagementAuditService() + */ + public RecordsManagementAuditService getRecordsManagementAuditService() + { + return (RecordsManagementAuditService)getService(RECORDS_MANAGEMENT_AUDIT_SERVICE); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry#getDictionaryService() + */ + @Override + public DispositionService getDispositionService() + { + return (DispositionService)getService(DISPOSITION_SERVICE); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RMActionExecuterAbstractBase.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RMActionExecuterAbstractBase.java new file mode 100644 index 0000000000..9ab72d3ae2 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RMActionExecuterAbstractBase.java @@ -0,0 +1,609 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditService; +import org.alfresco.module.org_alfresco_module_rm.capability.AbstractCapability; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.module.org_alfresco_module_rm.event.EventCompletionDetails; +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEvent; +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService; +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventType; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordService; +import org.alfresco.repo.action.executer.ActionExecuterAbstractBase; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ActionService; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.Period; +import org.alfresco.service.cmr.security.OwnableService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.PropertyCheck; +import org.springframework.beans.factory.BeanNameAware; +import org.springframework.extensions.surf.util.I18NUtil; +import org.springframework.util.StringUtils; + +/** + * Records management action executer base class + * + * @author Roy Wetherall + */ +public abstract class RMActionExecuterAbstractBase extends ActionExecuterAbstractBase + implements RecordsManagementAction, + RecordsManagementModel, + BeanNameAware +{ + /** Namespace service */ + protected NamespaceService namespaceService; + + /** Used to control transactional behaviour including post-commit auditing */ + protected TransactionService transactionService; + + /** Node service */ + protected NodeService nodeService; + + /** Dictionary service */ + protected DictionaryService dictionaryService; + + /** Content service */ + protected ContentService contentService; + + /** Action service */ + protected ActionService actionService; + + /** Records management action service */ + protected RecordsManagementAuditService recordsManagementAuditService; + + /** Records management action service */ + protected RecordsManagementActionService recordsManagementActionService; + + /** Records management service */ + protected RecordsManagementService recordsManagementService; + + /** Disposition service */ + protected DispositionService dispositionService; + + /** Vital record service */ + protected VitalRecordService vitalRecordService; + + /** Records management event service */ + protected RecordsManagementEventService recordsManagementEventService; + + /** Records management action service */ + protected RecordsManagementAdminService recordsManagementAdminService; + + /** Ownable service **/ + protected OwnableService ownableService; + + protected LinkedList capabilities = new LinkedList();; + + /** Default constructor */ + public RMActionExecuterAbstractBase() + { + } + + /** + * Set the namespace service + */ + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } + + /** + * Set the namespace service + */ + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + /** + * Set node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Set the dictionary service + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + /** + * Set the content service + */ + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + + /** + * Set action service + */ + public void setActionService(ActionService actionService) + { + this.actionService = actionService; + } + + /** + * Set the audit service that action details will be sent to + */ + public void setRecordsManagementAuditService(RecordsManagementAuditService recordsManagementAuditService) + { + this.recordsManagementAuditService = recordsManagementAuditService; + } + + /** + * Set records management service + */ + public void setRecordsManagementActionService(RecordsManagementActionService recordsManagementActionService) + { + this.recordsManagementActionService = recordsManagementActionService; + } + + /** + * Set records management service + */ + public void setRecordsManagementService(RecordsManagementService recordsManagementService) + { + this.recordsManagementService = recordsManagementService; + } + + /** + * Set the disposition service + */ + public void setDispositionService(DispositionService dispositionService) + { + this.dispositionService = dispositionService; + } + + /** + * @param vitalRecordService vital record service + */ + public void setVitalRecordService(VitalRecordService vitalRecordService) + { + this.vitalRecordService = vitalRecordService; + } + + /** + * Set records management event service + */ + public void setRecordsManagementEventService(RecordsManagementEventService recordsManagementEventService) + { + this.recordsManagementEventService = recordsManagementEventService; + } + + + /** + * Set the ownable service + * @param ownableSerice + */ + public void setOwnableService(OwnableService ownableService) + { + this.ownableService = ownableService; + } + + /** + * Register with a single capability + * @param capability + */ + public void setCapability(AbstractCapability capability) + { + capabilities.add(capability); + } + + /** + * Register with several capabilities + * @param capabilities + */ + public void setCapabilities(Collection capabilities) + { + this.capabilities.addAll(capabilities); + } + + public void setRecordsManagementAdminService(RecordsManagementAdminService recordsManagementAdminService) + { + this.recordsManagementAdminService = recordsManagementAdminService; + } + + public RecordsManagementAdminService getRecordsManagementAdminService() + { + return recordsManagementAdminService; + } + + /** + * Init method + */ + @Override + public void init() + { + PropertyCheck.mandatory(this, "namespaceService", namespaceService); + PropertyCheck.mandatory(this, "transactionService", transactionService); + PropertyCheck.mandatory(this, "nodeService", nodeService); + PropertyCheck.mandatory(this, "dictionaryService", dictionaryService); + PropertyCheck.mandatory(this, "contentService", contentService); + PropertyCheck.mandatory(this, "actionService", actionService); + PropertyCheck.mandatory(this, "transactionService", transactionService); + PropertyCheck.mandatory(this, "recordsManagementAuditService", recordsManagementAuditService); + PropertyCheck.mandatory(this, "recordsManagementActionService", recordsManagementActionService); + PropertyCheck.mandatory(this, "recordsManagementService", recordsManagementService); + PropertyCheck.mandatory(this, "recordsManagementAdminService", recordsManagementAdminService); + PropertyCheck.mandatory(this, "recordsManagementEventService", recordsManagementEventService); + for(AbstractCapability capability : capabilities) + { + capability.registerAction(this); + } + } + + /** + * @see org.alfresco.repo.action.CommonResourceAbstractBase#setBeanName(java.lang.String) + */ + @Override + public void setBeanName(String name) + { + this.name = name; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementAction#getName() + */ + public String getName() + { + return this.name; + } + + /* + * @see org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction#getLabel() + */ + public String getLabel() + { + String label = I18NUtil.getMessage(this.getTitleKey()); + + if (label == null) + { + // default to the name of the action with first letter capitalised + label = StringUtils.capitalize(this.name); + } + + return label; + } + + /* + * @see org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction#getDescription() + */ + public String getDescription() + { + String desc = I18NUtil.getMessage(this.getDescriptionKey()); + + if (desc == null) + { + // default to the name of the action with first letter capitalised + desc = StringUtils.capitalize(this.name); + } + + return desc; + } + + /** + * By default an action is not a disposition action + * + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementAction#isDispositionAction() + */ + public boolean isDispositionAction() + { + return false; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementAction#execute(org.alfresco.service.cmr.repository.NodeRef, java.util.Map) + */ + public RecordsManagementActionResult execute(NodeRef filePlanComponent, Map parameters) + { + isExecutableImpl(filePlanComponent, parameters, true); + + // Create the action + Action action = this.actionService.createAction(name); + action.setParameterValues(parameters); + + recordsManagementAuditService.auditRMAction(this, filePlanComponent, parameters); + + // Execute the action + this.actionService.executeAction(action, filePlanComponent); + + // Get the result + Object value = action.getParameterValue(ActionExecuterAbstractBase.PARAM_RESULT); + return new RecordsManagementActionResult(value); + } + + /** + * Function to pad a string with zero '0' characters to the required length + * + * @param s String to pad with leading zero '0' characters + * @param len Length to pad to + * + * @return padded string or the original if already at >=len characters + */ + protected String padString(String s, int len) + { + String result = s; + for (int i=0; i<(len - s.length()); i++) + { + result = "0" + result; + } + return result; + } + + /** + * By default there are no parameters. + * + * @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List) + */ + @Override + protected void addParameterDefinitions(List paramList) + { + // No parameters + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction#getProtectedProperties() + */ + public Set getProtectedProperties() + { + return Collections.emptySet(); + } + + + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction#getProtectedAspects() + */ + public Set getProtectedAspects() + { + return Collections.emptySet(); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction#isExecutable(org.alfresco.service.cmr.repository.NodeRef, java.util.Map) + */ + public boolean isExecutable(NodeRef filePlanComponent, Map parameters) + { + return isExecutableImpl(filePlanComponent, parameters, false); + } + + protected abstract boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException); + + /** + * By default, rmActions do not provide an implicit target nodeRef. + */ + public NodeRef getImplicitTargetNodeRef() + { + return null; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#updateNextDispositionAction(org.alfresco.service.cmr.repository.NodeRef) + */ + public void updateNextDispositionAction(NodeRef nodeRef) + { + // Get this disposition instructions for the node + DispositionSchedule di = dispositionService.getDispositionSchedule(nodeRef); + if (di != null) + { + // Get the current action node + NodeRef currentDispositionAction = null; + if (this.nodeService.hasAspect(nodeRef, ASPECT_DISPOSITION_LIFECYCLE) == true) + { + List assocs = this.nodeService.getChildAssocs(nodeRef, ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL); + if (assocs.size() > 0) + { + currentDispositionAction = assocs.get(0).getChildRef(); + } + } + + if (currentDispositionAction != null) + { + // Move it to the history association + this.nodeService.moveNode(currentDispositionAction, nodeRef, ASSOC_DISPOSITION_ACTION_HISTORY, ASSOC_DISPOSITION_ACTION_HISTORY); + } + + List dispositionActionDefinitions = di.getDispositionActionDefinitions(); + DispositionActionDefinition currentDispositionActionDefinition = null; + DispositionActionDefinition nextDispositionActionDefinition = null; + + if (currentDispositionAction == null) + { + if (dispositionActionDefinitions.isEmpty() == false) + { + // The next disposition action is the first action + nextDispositionActionDefinition = dispositionActionDefinitions.get(0); + } + } + else + { + // Get the current action + String currentADId = (String)this.nodeService.getProperty(currentDispositionAction, PROP_DISPOSITION_ACTION_ID); + currentDispositionActionDefinition = di.getDispositionActionDefinition(currentADId); + + // Get the next disposition action + int index = currentDispositionActionDefinition.getIndex(); + index++; + if (index < dispositionActionDefinitions.size()) + { + nextDispositionActionDefinition = dispositionActionDefinitions.get(index); + } + } + + if (nextDispositionActionDefinition != null) + { + if (this.nodeService.hasAspect(nodeRef, ASPECT_DISPOSITION_LIFECYCLE) == false) + { + // Add the disposition life cycle aspect + this.nodeService.addAspect(nodeRef, ASPECT_DISPOSITION_LIFECYCLE, null); + } + + // Create the properties + Map props = new HashMap(10); + + // Calculate the asOf date + Date asOfDate = null; + Period period = nextDispositionActionDefinition.getPeriod(); + if (period != null) + { + // Use NOW as the default context date + Date contextDate = new Date(); + + // Get the period properties value + QName periodProperty = nextDispositionActionDefinition.getPeriodProperty(); + if (periodProperty != null) + { + contextDate = (Date)this.nodeService.getProperty(nodeRef, periodProperty); + + if (contextDate == null) + { + // TODO For now we will use NOW to resolve MOB-1184 + //throw new AlfrescoRuntimeException("Date used to calculate disposition action asOf date is not set for property " + periodProperty.toString()); + contextDate = new Date(); + } + } + + // Calculate the as of date + asOfDate = period.getNextDate(contextDate); + } + + // Set the property values + props.put(PROP_DISPOSITION_ACTION_ID, nextDispositionActionDefinition.getId()); + props.put(PROP_DISPOSITION_ACTION, nextDispositionActionDefinition.getName()); + if (asOfDate != null) + { + props.put(PROP_DISPOSITION_AS_OF, asOfDate); + } + + // Create a new disposition action object + NodeRef dispositionActionNodeRef = this.nodeService.createNode( + nodeRef, + ASSOC_NEXT_DISPOSITION_ACTION, + ASSOC_NEXT_DISPOSITION_ACTION, + TYPE_DISPOSITION_ACTION, + props).getChildRef(); + + // Create the events + List events = nextDispositionActionDefinition.getEvents(); + for (RecordsManagementEvent event : events) + { + // For every event create an entry on the action + createEvent(event, dispositionActionNodeRef); + } + } + } + } + + /** + * Creates the given records management event for the given 'next action'. + * + * @param event The event to create + * @param nextActionNodeRef The next action node + * @return The created event NodeRef + */ + protected NodeRef createEvent(RecordsManagementEvent event, NodeRef nextActionNodeRef) + { + NodeRef eventNodeRef = null; + + Map eventProps = new HashMap(7); + eventProps.put(PROP_EVENT_EXECUTION_NAME, event.getName()); + // TODO display label + RecordsManagementEventType eventType = recordsManagementEventService.getEventType(event.getType()); + eventProps.put(PROP_EVENT_EXECUTION_AUTOMATIC, eventType.isAutomaticEvent()); + eventProps.put(PROP_EVENT_EXECUTION_COMPLETE, false); + + // Create the event execution object + this.nodeService.createNode(nextActionNodeRef, ASSOC_EVENT_EXECUTIONS, + ASSOC_EVENT_EXECUTIONS, TYPE_EVENT_EXECUTION, eventProps); + + return eventNodeRef; + } + + /** + * Calculates and updates the rma:dispositionEventsEligible + * property for the given next disposition action. + * + * @param nextAction The next disposition action + * @return The result of calculation + */ + protected boolean updateEventEligible(DispositionAction nextAction) + { + List events = nextAction.getEventCompletionDetails(); + + boolean eligible = false; + if (nextAction.getDispositionActionDefinition().eligibleOnFirstCompleteEvent() == false) + { + eligible = true; + for (EventCompletionDetails event : events) + { + if (event.isEventComplete() == false) + { + eligible = false; + break; + } + } + } + else + { + for (EventCompletionDetails event : events) + { + if (event.isEventComplete() == true) + { + eligible = true; + break; + } + } + } + + // Update the property with the eligible value + this.nodeService.setProperty(nextAction.getNodeRef(), PROP_DISPOSITION_EVENTS_ELIGIBLE, eligible); + + return eligible; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RMDispositionActionExecuterAbstractBase.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RMDispositionActionExecuterAbstractBase.java new file mode 100644 index 0000000000..72bd5a7861 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RMDispositionActionExecuterAbstractBase.java @@ -0,0 +1,393 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * @author Roy Wetherall + */ +public abstract class RMDispositionActionExecuterAbstractBase extends RMActionExecuterAbstractBase +{ + /** I18N */ + private static final String MSG_RECORD_NOT_DECLARED = "rm.action.record-not-declared"; + private static final String MSG_EXPECTED_RECORD_LEVEL = "rm.action.expected-record-level"; + private static final String MSG_NOT_ALL_RECORDS_DECLARED = "rm.action.not-all-records-declared"; + private static final String MSG_NOT_ELIGIBLE = "rm.action.not-eligible"; + private static final String MSG_NO_DISPOITION_INSTRUCTIONS = "rm.action.no-disposition-instructions"; + private static final String MSG_NO_DIS_LIFECYCLE_SET = "rm.action.no-disposition-lisfecycle-set"; + private static final String MSG_NEXT_DISP_NOT_SET = "rm.action.next-disp-not-set"; + private static final String MSG_NOT_NEXT_DISP = "rm.action.not-next-disp"; + private static final String MSG_NOT_RECORD_FOLDER = "rm.action.not-record-folder"; + + /** Indicates whether the eligibility of the record should be checked or not */ + // TODO add the capability to override this value using a property on the action + protected boolean checkEligibility = true; + + /** + * All children of this implementation are disposition actions. + * + * @see org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase#isDispositionAction() + */ + @Override + public boolean isDispositionAction() + { + return true; + } + + /** + * Indicates whether the disposition is marked complete + * + * @return boolean true if marked complete, false otherwise + */ + public boolean getSetDispositionActionComplete() + { + return true; + } + + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, + * org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + // + NodeRef nextDispositionActionNodeRef = getNextDispostionAction(actionedUponNodeRef); + + // Check the validity of the action (is it the next action, are we dealing with the correct type of object for + // the disposition level? + DispositionSchedule di = checkDispositionActionExecutionValidity(actionedUponNodeRef, nextDispositionActionNodeRef, true); + + // Check the eligibility of the action + if (checkEligibility == false || this.dispositionService.isNextDispositionActionEligible(actionedUponNodeRef) == true) + { + if (di.isRecordLevelDisposition() == true) + { + // Check that we do indeed have a record + if (this.recordsManagementService.isRecord(actionedUponNodeRef) == true) + { + // Can only execute disposition action on record if declared + if (this.recordsManagementService.isRecordDeclared(actionedUponNodeRef) == true) + { + // Indicate that the disposition action is underway + this.nodeService.setProperty(nextDispositionActionNodeRef, PROP_DISPOSITION_ACTION_STARTED_AT, new Date()); + this.nodeService.setProperty(nextDispositionActionNodeRef, PROP_DISPOSITION_ACTION_STARTED_BY, AuthenticationUtil.getRunAsUser()); + + // Execute record level disposition + executeRecordLevelDisposition(action, actionedUponNodeRef); + + if (this.nodeService.exists(nextDispositionActionNodeRef) == true && + getSetDispositionActionComplete() == true) + { + this.nodeService.setProperty(nextDispositionActionNodeRef, PROP_DISPOSITION_ACTION_COMPLETED_AT, new Date()); + this.nodeService.setProperty(nextDispositionActionNodeRef, PROP_DISPOSITION_ACTION_COMPLETED_BY, AuthenticationUtil.getRunAsUser()); + } + } + else + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_RECORD_NOT_DECLARED, getName(), actionedUponNodeRef.toString())); + } + } + else + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_EXPECTED_RECORD_LEVEL, getName(), actionedUponNodeRef.toString())); + } + } + else + { + if (this.recordsManagementService.isRecordFolder(actionedUponNodeRef) == true) + { + if (this.recordsManagementService.isRecordFolderDeclared(actionedUponNodeRef) == true) + { + // Indicate that the disposition action is underway + this.nodeService.setProperty(nextDispositionActionNodeRef, PROP_DISPOSITION_ACTION_STARTED_AT, new Date()); + this.nodeService.setProperty(nextDispositionActionNodeRef, PROP_DISPOSITION_ACTION_STARTED_BY, AuthenticationUtil.getRunAsUser()); + + executeRecordFolderLevelDisposition(action, actionedUponNodeRef); + + // Indicate that the disposition action is compelte + if (this.nodeService.exists(nextDispositionActionNodeRef) == true && + getSetDispositionActionComplete() == true) + { + this.nodeService.setProperty(nextDispositionActionNodeRef, PROP_DISPOSITION_ACTION_COMPLETED_AT, new Date()); + this.nodeService.setProperty(nextDispositionActionNodeRef, PROP_DISPOSITION_ACTION_COMPLETED_BY, AuthenticationUtil.getRunAsUser()); + } + + } + else + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NOT_ALL_RECORDS_DECLARED, getName(), actionedUponNodeRef.toString())); + } + } + else + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_EXPECTED_RECORD_LEVEL, getName(), actionedUponNodeRef.toString())); + } + + } + + if (this.nodeService.exists(actionedUponNodeRef) == true && getSetDispositionActionComplete() == true) + { + // Update the disposition schedule + updateNextDispositionAction(actionedUponNodeRef); + } + } + else + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NOT_ELIGIBLE, getName(), actionedUponNodeRef.toString())); + } + } + + /** + * @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List) + */ + @Override + protected void addParameterDefinitions(List paramList) + { + // TODO add the "checkEligibility" parameter + } + + /** + * @param action + * @param record + */ + protected abstract void executeRecordLevelDisposition(Action action, NodeRef record); + + /** + * @param action + * @param recordFolder + */ + protected abstract void executeRecordFolderLevelDisposition(Action action, NodeRef recordFolder); + + /** + * @param nodeRef + * @return + */ + protected DispositionSchedule checkDispositionActionExecutionValidity(NodeRef nodeRef, NodeRef nextDispositionActionNodeRef, boolean throwError) + { + // Check the node has associated disposition instructions + DispositionSchedule di = this.dispositionService.getDispositionSchedule(nodeRef); + if (di == null) + { + if (throwError) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NO_DISPOITION_INSTRUCTIONS, getName(), nodeRef.toString())); + } + else + { + return null; + } + } + + // Check the node has the disposition schedule aspect applied + if (this.nodeService.hasAspect(nodeRef, ASPECT_DISPOSITION_LIFECYCLE) == false) + { + if (throwError) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NO_DIS_LIFECYCLE_SET, getName(), nodeRef.toString())); + } + else + { + return null; + } + } + + // Check this the next disposition action + + NodeRef nextDispositionAction = nextDispositionActionNodeRef; + if (nextDispositionAction == null) + { + if (throwError) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NEXT_DISP_NOT_SET, getName(), nodeRef.toString())); + } + else + { + return null; + } + } + String actionName = (String) this.nodeService.getProperty(nextDispositionAction, PROP_DISPOSITION_ACTION); + if (actionName == null || actionName.equals(getName()) == false) + { + if (throwError) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NOT_NEXT_DISP, getName(), nodeRef.toString())); + } + else + { + return null; + } + } + + return di; + } + + /** + * Get the next disposition action node. Null if none present. + * + * @param nodeRef + * the disposable node reference + * @return NodeRef the next disposition action, null if none + */ + private NodeRef getNextDispostionAction(NodeRef nodeRef) + { + NodeRef result = null; + List assocs = this.nodeService.getChildAssocs(nodeRef, ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL); + if (assocs.size() != 0) + { + result = assocs.get(0).getChildRef(); + } + return result; + } + + @Override + public Set getProtectedProperties() + { + HashSet qnames = new HashSet(); + qnames.add(PROP_DISPOSITION_ACTION_STARTED_AT); + qnames.add(PROP_DISPOSITION_ACTION_STARTED_BY); + qnames.add(PROP_DISPOSITION_ACTION_COMPLETED_AT); + qnames.add(PROP_DISPOSITION_ACTION_COMPLETED_BY); + return qnames; + } + + @Override + protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + // Check the validity of the action (is it the next action, are we dealing with the correct type of object for + // the disposition level? + // + NodeRef nextDispositionActionNodeRef = getNextDispostionAction(filePlanComponent); + + DispositionSchedule di = checkDispositionActionExecutionValidity(filePlanComponent, nextDispositionActionNodeRef, throwException); + + if(di == null) + { + if (throwException) + { + throw new AlfrescoRuntimeException("Null disposition"); + } + else + { + return false; + } + } + + // Check the eligibility of the action + if (checkEligibility == false || this.dispositionService.isNextDispositionActionEligible(filePlanComponent) == true) + { + if (di.isRecordLevelDisposition() == true) + { + // Check that we do indeed have a record + if (this.recordsManagementService.isRecord(filePlanComponent) == true) + { + // Can only execute disposition action on record if declared + if (this.recordsManagementService.isRecordDeclared(filePlanComponent) == true) + { + return true; + } + else + { + if (throwException) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_RECORD_NOT_DECLARED, getName(), filePlanComponent.toString())); + } + else + { + return false; + } + } + } + else + { + if (throwException) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_EXPECTED_RECORD_LEVEL, getName(), filePlanComponent.toString())); + } + else + { + return false; + } + } + } + else + { + if (this.recordsManagementService.isRecordFolder(filePlanComponent) == true) + { + if (this.recordsManagementService.isRecordFolderDeclared(filePlanComponent) == true) + { + return true; + } + else + { + if (throwException) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NOT_ALL_RECORDS_DECLARED, getName(), filePlanComponent.toString())); + } + else + { + return false; + } + } + } + else + { + if (throwException) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NOT_RECORD_FOLDER, getName(), filePlanComponent.toString())); + } + else + { + return false; + } + } + + } + + } + else + { + if (throwException) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NOT_ELIGIBLE, getName(), filePlanComponent.toString())); + } + else + { + return false; + } + } + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RecordsManagementAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RecordsManagementAction.java new file mode 100644 index 0000000000..1e64b22162 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RecordsManagementAction.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action; + +import java.io.Serializable; +import java.util.Map; +import java.util.Set; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + + +/** + * Record Management Action + * + * @author Roy Wetherall + */ +public interface RecordsManagementAction +{ + /** + * Get the name of the action + * + * @return String action name + */ + public String getName(); + + /** + * Get the label of the action + * + * @return String action label + */ + public String getLabel(); + + /** + * Get the description of the action + * + * @return String action description + */ + public String getDescription(); + + /** + * Indicates whether this is a disposition action or not + * + * @return boolean true if a disposition action, false otherwise + */ + boolean isDispositionAction(); + + /** + * Execution of the action + * + * @param filePlanComponent file plan component the action is executed upon + * @param parameters action parameters + */ + public RecordsManagementActionResult execute(NodeRef filePlanComponent, Map parameters); + + + /** + * Can this action be executed? + * Does it meet all of its entry requirements - EXCEPT permission checks. + * + * @param filePlanComponent file plan component the action is executed upon + * @param parameters action parameters + * @return + */ + public boolean isExecutable(NodeRef filePlanComponent, Map parameters); + + + /** + * Get a set of properties that should only be updated via this or other action. + * These properties will be rejected by updates via the generic public services, such as the NodeService. + * + * @return the set of protected properties + */ + public Set getProtectedProperties(); + + /** + * Get a set of aspects that should be updated via this or other actions. + * The aspect can not be added via public services, such as the NodeService. + * @return + */ + public Set getProtectedAspects(); + + /** + * Some admin-related rmActions execute against a target nodeRef which is not provided + * by the calling code, but is instead an implementation detail of the action. + * + * @return the target nodeRef + */ + public NodeRef getImplicitTargetNodeRef(); +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RecordsManagementActionResult.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RecordsManagementActionResult.java new file mode 100644 index 0000000000..583beefe40 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RecordsManagementActionResult.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2009-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.module.org_alfresco_module_rm.action; + +/** + * Records management action result. + * + * @author Roy Wetherall + */ +public class RecordsManagementActionResult +{ + /** Result value */ + private Object value; + + /** + * Constructor. + * + * @param value result value + */ + public RecordsManagementActionResult(Object value) + { + this.value = value; + } + + /** + * @return result value + */ + public Object getValue() + { + return this.value; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RecordsManagementActionService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RecordsManagementActionService.java new file mode 100644 index 0000000000..6d943d7412 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RecordsManagementActionService.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +import org.alfresco.service.cmr.repository.NodeRef; + + +/** + * Records management action service interface + * + * @author Roy Wetherall + */ +public interface RecordsManagementActionService +{ + /** + * Get a list of the available records management actions + * + * @return List records management actions + */ + List getRecordsManagementActions(); + + /** + * Get a list of the available disposition actions. A disposition action is a records + * management action that can be used when defining disposition instructions. + * + * @return List disposition actions + */ + List getDispositionActions(); + + /** + * Gets the named records management action + * + * @param name The name of the RM action to retrieve + * @return The RecordsManagementAction or null if it doesn't exist + */ + RecordsManagementAction getRecordsManagementAction(String name); + + /** + * Gets the named disposition action + * + * @param name The name of the disposition action to retrieve + * @return The RecordsManagementAction or null if it doesn't exist + */ + RecordsManagementAction getDispositionAction(String name); + + /** + * Execute a records management action + * + * @param nodeRef node reference to a rm container, rm folder or record + * @param name action name + */ + RecordsManagementActionResult executeRecordsManagementAction(NodeRef nodeRef, String name); + + /** + * Execute a records management action against several nodes + * + * @param nodeRefs node references to rm containers, rm folders or records + * @param name action name + */ + Map executeRecordsManagementAction(List nodeRefs, String name); + + /** + * Execute a records management action + * + * @param nodeRef node reference to a rm container, rm folder or record + * @param name action name + * @param parameters action parameters + */ + RecordsManagementActionResult executeRecordsManagementAction(NodeRef nodeRef, String name, Map parameters); + + /** + * Execute a records management action against several nodes + * + * @param nodeRefs node references to rm containers, rm folders or records + * @param name action name + * @param parameters action parameters + */ + Map executeRecordsManagementAction(List nodeRefs, String name, Map parameters); + + /** + * Execute a records management action. The nodeRef against which the action is to be + * executed must be provided by the RecordsManagementAction implementation. + * + * @param name action name + * @param parameters action parameters + */ + RecordsManagementActionResult executeRecordsManagementAction(String name, Map parameters); + + /** + * Register records management action + * + * @param rmAction records management action + */ + void register(RecordsManagementAction rmAction); +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RecordsManagementActionServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RecordsManagementActionServiceImpl.java new file mode 100644 index 0000000000..ac6eab0aaf --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RecordsManagementActionServiceImpl.java @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPoliciesUtil; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.BeforeRMActionExecution; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.OnRMActionExecution; +import org.alfresco.repo.policy.ClassPolicyDelegate; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * Records Management Action Service Implementation + * + * @author Roy Wetherall + */ +public class RecordsManagementActionServiceImpl implements RecordsManagementActionService +{ + /** I18N */ + private static final String MSG_NOT_DEFINED = "rm.action.not-defined"; + private static final String MSG_NO_IMPLICIT_NODEREF = "rm.action.no-implicit-noderef"; + + /** Logger */ + private static Log logger = LogFactory.getLog(RecordsManagementActionServiceImpl.class); + + /** Registered records management actions */ + private Map rmActions = new HashMap(6); + private Map dispositionActions = new HashMap(4); + + /** Policy component */ + PolicyComponent policyComponent; + + /** Node service */ + NodeService nodeService; + + /** Policy delegates */ + private ClassPolicyDelegate beforeRMActionExecutionDelegate; + private ClassPolicyDelegate onRMActionExecutionDelegate; + + /** + * Set the policy component + * + * @param policyComponent policy component + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * Set the node service + * + * @param nodeService node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Initialise RM action service + */ + public void init() + { + // Register the various policies + beforeRMActionExecutionDelegate = policyComponent.registerClassPolicy(BeforeRMActionExecution.class); + onRMActionExecutionDelegate = policyComponent.registerClassPolicy(OnRMActionExecution.class); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementActionService#register(org.alfresco.module.org_alfresco_module_rm.RecordsManagementAction) + */ + public void register(RecordsManagementAction rmAction) + { + if (this.rmActions.containsKey(rmAction.getName()) == false) + { + if (logger.isDebugEnabled()) + { + logger.debug("Registering rmAction " + rmAction); + } + this.rmActions.put(rmAction.getName(), rmAction); + + if (rmAction.isDispositionAction() == true) + { + this.dispositionActions.put(rmAction.getName(), rmAction); + } + } + } + + /** + * Invoke beforeRMActionExecution policy + * + * @param nodeRef node reference + * @param name action name + * @param parameters action parameters + */ + protected void invokeBeforeRMActionExecution(NodeRef nodeRef, String name, Map parameters) + { + // get qnames to invoke against + Set qnames = RecordsManagementPoliciesUtil.getTypeAndAspectQNames(nodeService, nodeRef); + // execute policy for node type and aspects + BeforeRMActionExecution policy = beforeRMActionExecutionDelegate.get(qnames); + policy.beforeRMActionExecution(nodeRef, name, parameters); + } + + /** + * Invoke onRMActionExecution policy + * + * @param nodeRef node reference + * @param name action name + * @param parameters action parameters + */ + protected void invokeOnRMActionExecution(NodeRef nodeRef, String name, Map parameters) + { + // get qnames to invoke against + Set qnames = RecordsManagementPoliciesUtil.getTypeAndAspectQNames(nodeService, nodeRef); + // execute policy for node type and aspects + OnRMActionExecution policy = onRMActionExecutionDelegate.get(qnames); + policy.onRMActionExecution(nodeRef, name, parameters); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementActionService#getRecordsManagementActions() + */ + public List getRecordsManagementActions() + { + List result = new ArrayList(this.rmActions.size()); + result.addAll(this.rmActions.values()); + return Collections.unmodifiableList(result); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService#getDispositionActions(org.alfresco.service.cmr.repository.NodeRef) + */ + public List getDispositionActions(NodeRef nodeRef) + { + String userName = AuthenticationUtil.getFullyAuthenticatedUser(); + List result = new ArrayList(this.rmActions.size()); + + for (RecordsManagementAction action : this.rmActions.values()) + { + // TODO check the permissions on the action ... + } + + return Collections.unmodifiableList(result); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementActionService#getDispositionActionDefinitions() + */ + public List getDispositionActions() + { + List result = new ArrayList(this.rmActions.size()); + result.addAll(this.dispositionActions.values()); + return Collections.unmodifiableList(result); + } + + /* + * @see org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService#getDispositionAction(java.lang.String) + */ + public RecordsManagementAction getDispositionAction(String name) + { + return this.dispositionActions.get(name); + } + + /* + * @see org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService#getRecordsManagementAction(java.lang.String) + */ + public RecordsManagementAction getRecordsManagementAction(String name) + { + return this.rmActions.get(name); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementActionService#executeRecordsManagementAction(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public RecordsManagementActionResult executeRecordsManagementAction(NodeRef nodeRef, String name) + { + return executeRecordsManagementAction(nodeRef, name, null); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementActionService#executeRecordsManagementAction(java.util.List, java.lang.String) + */ + public Map executeRecordsManagementAction(List nodeRefs, String name) + { + return executeRecordsManagementAction(nodeRefs, name, null); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementActionService#executeRecordsManagementAction(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, java.util.Map) + */ + public RecordsManagementActionResult executeRecordsManagementAction(NodeRef nodeRef, String name, Map parameters) + { + if (logger.isDebugEnabled()) + { + logger.debug("Executing record management action on " + nodeRef); + logger.debug(" actionName = " + name); + logger.debug(" parameters = " + parameters); + } + + RecordsManagementAction rmAction = this.rmActions.get(name); + if (rmAction == null) + { + String msg = I18NUtil.getMessage(MSG_NOT_DEFINED, name); + if (logger.isWarnEnabled()) + { + logger.warn(msg); + } + throw new AlfrescoRuntimeException(msg); + } + + // Execute action + invokeBeforeRMActionExecution(nodeRef, name, parameters); + RecordsManagementActionResult result = rmAction.execute(nodeRef, parameters); + if (nodeService.exists(nodeRef) == true) + { + invokeOnRMActionExecution(nodeRef, name, parameters); + } + + return result; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementActionService#executeRecordsManagementAction(java.lang.String, java.util.Map) + */ + public RecordsManagementActionResult executeRecordsManagementAction(String name, Map parameters) + { + RecordsManagementAction rmAction = rmActions.get(name); + + NodeRef implicitTargetNode = rmAction.getImplicitTargetNodeRef(); + if (implicitTargetNode == null) + { + String msg = I18NUtil.getMessage(MSG_NO_IMPLICIT_NODEREF, name); + if (logger.isWarnEnabled()) + { + logger.warn(msg); + } + throw new AlfrescoRuntimeException(msg); + } + else + { + return this.executeRecordsManagementAction(implicitTargetNode, name, parameters); + } + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementActionService#executeRecordsManagementAction(java.util.List, java.lang.String, java.util.Map) + */ + public Map executeRecordsManagementAction(List nodeRefs, String name, Map parameters) + { + // Execute the action on each node in the list + Map results = new HashMap(nodeRefs.size()); + for (NodeRef nodeRef : nodeRefs) + { + RecordsManagementActionResult result = executeRecordsManagementAction(nodeRef, name, parameters); + results.put(nodeRef, result); + } + + return results; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/ScheduledDispositionJob.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/ScheduledDispositionJob.java new file mode 100644 index 0000000000..d4dc38e8bf --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/ScheduledDispositionJob.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action; + +import java.util.Calendar; +import java.util.List; + +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; + +/** + * Scheduled disposition job. + * + * Automatically cuts off eligible nodes. + * + * @author Roy Wetherall + */ +public class ScheduledDispositionJob implements Job +{ + /** Logger */ + private static Log logger = LogFactory.getLog(ScheduledDispositionJob.class); + + /** + * @see org.quartz.Job#execute(org.quartz.JobExecutionContext) + */ + public void execute(JobExecutionContext context) throws JobExecutionException + { + RecordsManagementActionService rmActionService + = (RecordsManagementActionService)context.getJobDetail().getJobDataMap().get("recordsManagementActionService"); + NodeService nodeService = (NodeService)context.getJobDetail().getJobDataMap().get("nodeService"); + + + // Calculate the date range used in the query + Calendar cal = Calendar.getInstance(); + String year = String.valueOf(cal.get(Calendar.YEAR)); + String month = String.valueOf(cal.get(Calendar.MONTH) + 1); + String dayOfMonth = String.valueOf(cal.get(Calendar.DAY_OF_MONTH)); + + //TODO These pad() calls are in RMActionExecuterAbstractBase. I've copied them + // here as I have no access to that class. + + final String currentDate = padString(year, 2) + "-" + padString(month, 2) + + "-" + padString(dayOfMonth, 2) + "T00:00:00.00Z"; + + if (logger.isDebugEnabled() == true) + { + StringBuilder msg = new StringBuilder(); + msg.append("Executing ") + .append(this.getClass().getSimpleName()) + .append(" with currentDate ") + .append(currentDate); + logger.debug(msg.toString()); + } + + //TODO Copied the 1970 start date from the old RM JavaScript impl. + String dateRange = "[\"1970-01-01T00:00:00.00Z\" TO \"" + currentDate + "\"]"; + + // Execute the query and process the results + String query = "+ASPECT:\"rma:record\" +ASPECT:\"rma:dispositionSchedule\" +@rma\\:dispositionAsOf:" + dateRange; + + SearchService search = (SearchService)context.getJobDetail().getJobDataMap().get("searchService"); + ResultSet results = search.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_LUCENE, query); + + List resultNodes = results.getNodeRefs(); + results.close(); + + if (logger.isDebugEnabled() == true) + { + StringBuilder msg = new StringBuilder(); + msg.append("Found ") + .append(resultNodes.size()) + .append(" records eligible for disposition."); + logger.debug(msg.toString()); + } + + for (NodeRef node : resultNodes ) + { + String dispActionName = (String)nodeService.getProperty(node, RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME); + + // Only automatically execute "cutoff" actions. + // destroy and transfer and anything else should be manual for now + if (dispActionName != null && dispActionName.equalsIgnoreCase("cutoff")) + { + rmActionService.executeRecordsManagementAction(node, dispActionName); + + if (logger.isDebugEnabled() == true) + { + logger.debug("Performing " + dispActionName + " dispoition action on disposable item " + node.toString()); + } + } + } + } + + //TODO This has been pasted out of RMActionExecuterAbstractBase. To be relocated. + private String padString(String s, int len) + { + String result = s; + for (int i=0; i<(len - s.length()); i++) + { + result = "0" + result; + } + return result; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/ApplyCustomTypeAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/ApplyCustomTypeAction.java new file mode 100644 index 0000000000..01c5b71dcf --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/ApplyCustomTypeAction.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action.impl; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; +import org.alfresco.repo.action.ParameterDefinitionImpl; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * This class applies the aspect specified in the spring bean property customTypeAspect. + * It is used to apply one of the 4 "custom type" aspects from the DOD 5015 model. + * + * @author Neil McErlean + */ +public class ApplyCustomTypeAction extends RMActionExecuterAbstractBase +{ + /** I18N */ + private static final String MSG_ACTIONED_UPON_NOT_RECORD = "rm.action.actioned-upon-not-record"; + private static final String MSG_CUSTOM_ASPECT_NOT_RECOGNISED = "rm.action.custom-aspect-not-recognised"; + + private static Log logger = LogFactory.getLog(ApplyCustomTypeAction.class); + private QName customTypeAspect; + private List parameterDefinitions; + + public void setCustomTypeAspect(String customTypeAspect) + { + this.customTypeAspect = QName.createQName(customTypeAspect, namespaceService); + } + + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + if (logger.isDebugEnabled()) + { + logger.debug("Executing action [" + action.getActionDefinitionName() + "] on " + actionedUponNodeRef); + } + + // Apply the appropriate aspect and set the properties. + Map aspectProps = getPropertyValues(action); + this.nodeService.addAspect(actionedUponNodeRef, customTypeAspect, aspectProps); + } + + /** + * This method extracts the properties from the custom type's aspect. + * @see #getCustomTypeAspect() + */ + @Override + protected final void addParameterDefinitions(List paramList) + { + AspectDefinition aspectDef = dictionaryService.getAspect(customTypeAspect); + for (PropertyDefinition propDef : aspectDef.getProperties().values()) + { + QName propName = propDef.getName(); + QName propType = propDef.getDataType().getName(); + paramList.add(new ParameterDefinitionImpl(propName.toPrefixString(), propType, propDef.isMandatory(), null)); + } + } + + /** + * This method converts a Map of String, Serializable to a Map of QName, Serializable. + * To do this, it assumes that each parameter name is a String representing a qname + * of the form prefix:localName. + */ + private Map getPropertyValues(Action action) + { + Map paramValues = action.getParameterValues(); + + Map result = new HashMap(paramValues.size()); + for (String paramName : paramValues.keySet()) + { + QName propQName = QName.createQName(paramName, this.namespaceService); + result.put(propQName, paramValues.get(paramName)); + } + + return result; + } + + @Override + public boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + + if (recordsManagementService.isRecord(filePlanComponent)) + { + return true; + } + else + { + if (throwException) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_ACTIONED_UPON_NOT_RECORD, this.getClass().getSimpleName(), filePlanComponent.toString())); + } + else + { + return false; + } + } + } + + @Override + protected synchronized List getParameterDefintions() + { + // We can take these parameter definitions from the properties defined in the dod model. + if (this.parameterDefinitions == null) + { + AspectDefinition aspectDefinition = dictionaryService.getAspect(customTypeAspect); + if (aspectDefinition == null) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_CUSTOM_ASPECT_NOT_RECOGNISED, customTypeAspect)); + } + + Map props = aspectDefinition.getProperties(); + + this.parameterDefinitions = new ArrayList(props.size()); + + for (QName qn : props.keySet()) + { + String paramName = qn.toPrefixString(namespaceService); + QName paramType = props.get(qn).getDataType().getName(); + boolean paramIsMandatory = props.get(qn).isMandatory(); + parameterDefinitions.add(new ParameterDefinitionImpl(paramName, paramType, paramIsMandatory, null)); + } + } + return parameterDefinitions; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/BroadcastDispositionActionDefinitionUpdateAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/BroadcastDispositionActionDefinitionUpdateAction.java new file mode 100644 index 0000000000..f0068e8066 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/BroadcastDispositionActionDefinitionUpdateAction.java @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action.impl; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; +import org.alfresco.module.org_alfresco_module_rm.event.EventCompletionDetails; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.Period; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Action to implement the consequences of a change to the value of the DispositionActionDefinition + * properties. When these properties are changed on a disposition schedule, then any associated + * disposition actions may need to be updated as a consequence. + * + * @author Neil McErlean + */ +public class BroadcastDispositionActionDefinitionUpdateAction extends RMActionExecuterAbstractBase +{ + /** Logger */ + private static Log logger = LogFactory.getLog(BroadcastDispositionActionDefinitionUpdateAction.class); + + public static final String NAME = "broadcastDispositionActionDefinitionUpdate"; + public static final String CHANGED_PROPERTIES = "changedProperties"; + + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, + * org.alfresco.service.cmr.repository.NodeRef) + */ + @SuppressWarnings("unchecked") + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + if (RecordsManagementModel.TYPE_DISPOSITION_ACTION_DEFINITION.equals(nodeService.getType(actionedUponNodeRef)) == false) + { + return; + } + + List changedProps = (List)action.getParameterValue(CHANGED_PROPERTIES); + + // Navigate up the containment hierarchy to get the record category grandparent and schedule. + NodeRef dispositionScheduleNode = nodeService.getPrimaryParent(actionedUponNodeRef).getParentRef(); + NodeRef rmContainer = nodeService.getPrimaryParent(dispositionScheduleNode).getParentRef(); + DispositionSchedule dispositionSchedule = dispositionService.getAssociatedDispositionSchedule(rmContainer); + + List disposableItems = dispositionService.getDisposableItems(dispositionSchedule); + for (NodeRef disposableItem : disposableItems) + { + updateDisposableItem(dispositionSchedule, disposableItem, actionedUponNodeRef, changedProps); + } + } + + /** + * + * @param ds + * @param disposableItem + * @param dispositionActionDefinition + * @param changedProps + */ + private void updateDisposableItem(DispositionSchedule ds, NodeRef disposableItem, NodeRef dispositionActionDefinition, List changedProps) + { + // We need to check that this folder is under the management of the disposition schedule that + // has been updated + DispositionSchedule itemDs = dispositionService.getDispositionSchedule(disposableItem); + if (itemDs != null && + itemDs.getNodeRef().equals(ds.getNodeRef()) == true) + { + if (this.nodeService.hasAspect(disposableItem, ASPECT_DISPOSITION_LIFECYCLE)) + { + // disposition lifecycle already exists for node so process changes + processActionDefinitionChanges(dispositionActionDefinition, changedProps, disposableItem); + } + else + { + // disposition lifecycle does not exist on the node so setup disposition + updateNextDispositionAction(disposableItem); + } + } + } + + /** + * Processes all the changes applied to the given disposition + * action definition node for the given record or folder node. + * + * @param dispositionActionDef The disposition action definition node + * @param changedProps The set of properties changed on the action definition + * @param recordOrFolder The record or folder the changes potentially need to be applied to + */ + private void processActionDefinitionChanges(NodeRef dispositionActionDef, List changedProps, NodeRef recordOrFolder) + { + // check that the step being edited is the current step for the folder, + // if not, the change has no effect on the current step so ignore + DispositionAction nextAction = dispositionService.getNextDispositionAction(recordOrFolder); + if (doesChangedStepAffectNextAction(dispositionActionDef, nextAction)) + { + // the change does effect the nextAction for this node + // so go ahead and determine what needs updating + if (changedProps.contains(PROP_DISPOSITION_PERIOD)) + { + persistPeriodChanges(dispositionActionDef, nextAction); + } + + if (changedProps.contains(PROP_DISPOSITION_EVENT) || changedProps.contains(PROP_DISPOSITION_EVENT_COMBINATION)) + { + persistEventChanges(dispositionActionDef, nextAction); + } + + if (changedProps.contains(PROP_DISPOSITION_ACTION_NAME)) + { + String action = (String)nodeService.getProperty(dispositionActionDef, PROP_DISPOSITION_ACTION_NAME); + nodeService.setProperty(nextAction.getNodeRef(), PROP_DISPOSITION_ACTION, action); + } + } + } + + /** + * Determines whether the disposition action definition (step) being + * updated has any effect on the given next action + * + * @param dispositionActionDef The disposition action definition node + * @param nextAction The next disposition action + * @return true if the step change affects the next action + */ + private boolean doesChangedStepAffectNextAction(NodeRef dispositionActionDef, + DispositionAction nextAction) + { + boolean affectsNextAction = false; + + if (dispositionActionDef != null && nextAction != null) + { + // check whether the id of the action definition node being changed + // is the same as the id of the next action + String nextActionId = nextAction.getId(); + if (dispositionActionDef.getId().equals(nextActionId)) + { + affectsNextAction = true; + } + } + + return affectsNextAction; + } + + /** + * Persists any changes made to the period on the given disposition action + * definition on the given next action. + * + * @param dispositionActionDef The disposition action definition node + * @param nextAction The next disposition action + */ + private void persistPeriodChanges(NodeRef dispositionActionDef, DispositionAction nextAction) + { + Date newAsOfDate = null; + Period dispositionPeriod = (Period)nodeService.getProperty(dispositionActionDef, PROP_DISPOSITION_PERIOD); + + if (dispositionPeriod != null) + { + // calculate the new as of date as we have been provided a new period + Date now = new Date(); + newAsOfDate = dispositionPeriod.getNextDate(now); + } + + if (logger.isDebugEnabled()) + { + logger.debug("Set disposition as of date for next action '" + nextAction.getName() + + "' (" + nextAction.getNodeRef() + ") to: " + newAsOfDate); + } + + this.nodeService.setProperty(nextAction.getNodeRef(), PROP_DISPOSITION_AS_OF, newAsOfDate); + } + + /** + * Persists any changes made to the events on the given disposition action + * definition on the given next action. + * + * @param dispositionActionDef The disposition action definition node + * @param nextAction The next disposition action + */ + @SuppressWarnings("unchecked") + private void persistEventChanges(NodeRef dispositionActionDef, DispositionAction nextAction) + { + // go through the current events on the next action and remove any that are not present any more + List stepEvents = (List)nodeService.getProperty(dispositionActionDef, PROP_DISPOSITION_EVENT); + List eventsList = nextAction.getEventCompletionDetails(); + List nextActionEvents = new ArrayList(eventsList.size()); + for (EventCompletionDetails event : eventsList) + { + // take note of the event names present on the next action + String eventName = event.getEventName(); + nextActionEvents.add(eventName); + + // if the event has been removed delete from next action + if (stepEvents != null && stepEvents.contains(event.getEventName()) == false) + { + // remove the child association representing the event + nodeService.removeChild(nextAction.getNodeRef(), event.getNodeRef()); + + if (logger.isDebugEnabled()) + logger.debug("Removed '" + eventName + "' from next action '" + nextAction.getName() + + "' (" + nextAction.getNodeRef() + ")"); + } + } + + // go through the disposition action definition step events and add any new ones + if (stepEvents != null) + { + for (String eventName : stepEvents) + { + if (!nextActionEvents.contains(eventName)) + { + createEvent(recordsManagementEventService.getEvent(eventName), nextAction.getNodeRef()); + + if (logger.isDebugEnabled()) + { + logger.debug("Added '" + eventName + "' to next action '" + nextAction.getName() + + "' (" + nextAction.getNodeRef() + ")"); + } + } + } + } + + // finally since events may have changed re-calculate the events eligible flag + boolean eligible = updateEventEligible(nextAction); + + if (logger.isDebugEnabled()) + { + logger.debug("Set events eligible flag to '" + eligible + "' for next action '" + nextAction.getName() + + "' (" + nextAction.getNodeRef() + ")"); + } + } + + + @Override + protected void addParameterDefinitions(List paramList) + { + // Intentionally empty + } + + @Override + public boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + return true; + } + + @Override + public Set getProtectedProperties() + { + HashSet qnames = new HashSet(); + qnames.add(PROP_DISPOSITION_AS_OF); + qnames.add(PROP_DISPOSITION_EVENT); + qnames.add(PROP_DISPOSITION_EVENT_COMBINATION); + qnames.add(PROP_DISPOSITION_EVENTS_ELIGIBLE); + return qnames; + } + + @Override + public Set getProtectedAspects() + { + return Collections.emptySet(); + } + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/CloseRecordFolderAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/CloseRecordFolderAction.java new file mode 100644 index 0000000000..6c59750de6 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/CloseRecordFolderAction.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action.impl; + +import java.io.Serializable; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * Action to close the records folder + * + * @author Roy Wetherall + */ +public class CloseRecordFolderAction extends RMActionExecuterAbstractBase +{ + /** I18N */ + private static final String MSG_CLOSE_RECORD_FOLDER_NOT_FOLDER = "rm.action.close-record-folder-not-folder"; + + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + // TODO check that the user in question has the correct permissions to close a records folder + + if (this.recordsManagementService.isRecordFolder(actionedUponNodeRef) == true) + { + Boolean isClosed = (Boolean)this.nodeService.getProperty(actionedUponNodeRef, PROP_IS_CLOSED); + if (Boolean.FALSE.equals(isClosed) == true) + { + this.nodeService.setProperty(actionedUponNodeRef, PROP_IS_CLOSED, true); + } + } + else + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_CLOSE_RECORD_FOLDER_NOT_FOLDER, actionedUponNodeRef.toString())); + } + } + + /** + * @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List) + */ + @Override + protected void addParameterDefinitions(List paramList) + { + // TODO Auto-generated method stub + + } + + @Override + public Set getProtectedProperties() + { + HashSet qnames = new HashSet(); + qnames.add(PROP_IS_CLOSED); + return qnames; + } + + @Override + protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + if (this.recordsManagementService.isRecordFolder(filePlanComponent)) + { + return true; + } + else + { + if (throwException) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_CLOSE_RECORD_FOLDER_NOT_FOLDER, filePlanComponent.toString())); + } + else + { + return false; + } + } + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/CompleteEventAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/CompleteEventAction.java new file mode 100644 index 0000000000..50d674030d --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/CompleteEventAction.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action.impl; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction; +import org.alfresco.module.org_alfresco_module_rm.event.EventCompletionDetails; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * Complete event action + * + * @author Roy Wetherall + */ +public class CompleteEventAction extends RMActionExecuterAbstractBase +{ + /** I18N */ + private static final String MSG_EVENT_NO_DISP_LC = "rm.action.event-no-disp-lc"; + + public static final String PARAM_EVENT_NAME = "eventName"; + public static final String PARAM_EVENT_COMPLETED_BY = "eventCompletedBy"; + public static final String PARAM_EVENT_COMPLETED_AT = "eventCompletedAt"; + + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + String eventName = (String)action.getParameterValue(PARAM_EVENT_NAME); + String eventCompletedBy = (String)action.getParameterValue(PARAM_EVENT_COMPLETED_BY); + Date eventCompletedAt = (Date)action.getParameterValue(PARAM_EVENT_COMPLETED_AT); + + if (this.nodeService.hasAspect(actionedUponNodeRef, ASPECT_DISPOSITION_LIFECYCLE) == true) + { + // Get the next disposition action + DispositionAction da = this.dispositionService.getNextDispositionAction(actionedUponNodeRef); + if (da != null) + { + // Get the disposition event + EventCompletionDetails event = getEvent(da, eventName); + if (event != null) + { + // Update the event so that it is complete + NodeRef eventNodeRef = event.getNodeRef(); + Map props = this.nodeService.getProperties(eventNodeRef); + props.put(PROP_EVENT_EXECUTION_COMPLETE, true); + props.put(PROP_EVENT_EXECUTION_COMPLETED_AT, eventCompletedAt); + props.put(PROP_EVENT_EXECUTION_COMPLETED_BY, eventCompletedBy); + this.nodeService.setProperties(eventNodeRef, props); + + // Check to see if the events eligible property needs to be updated + updateEventEligible(da); + + } + else + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_EVENT_NO_DISP_LC, eventName)); + } + } + } + } + + /** + * Get the event from the dispostion action + * + * @param da + * @param eventName + * @return + */ + private EventCompletionDetails getEvent(DispositionAction da, String eventName) + { + EventCompletionDetails result = null; + List events = da.getEventCompletionDetails(); + for (EventCompletionDetails event : events) + { + if (eventName.equals(event.getEventName()) == true) + { + result = event; + break; + } + } + return result; + } + + /** + * @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List) + */ + @Override + protected void addParameterDefinitions(List paramList) + { + // TODO add parameter definitions .... + // eventId, executeBy, executedAt + + } + + @Override + public Set getProtectedProperties() + { + HashSet qnames = new HashSet(); + qnames.add(PROP_EVENT_EXECUTION_COMPLETE); + qnames.add(PROP_EVENT_EXECUTION_COMPLETED_AT); + qnames.add(PROP_EVENT_EXECUTION_COMPLETED_BY); + return qnames; + } + + + + @Override + public Set getProtectedAspects() + { + HashSet qnames = new HashSet(); + qnames.add(ASPECT_DISPOSITION_LIFECYCLE); + return qnames; + } + + @Override + protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + String eventName = null; + if(parameters != null) + { + eventName = (String) parameters.get(PARAM_EVENT_NAME); + } + + if (this.nodeService.hasAspect(filePlanComponent, ASPECT_DISPOSITION_LIFECYCLE)) + { + // Get the next disposition action + DispositionAction da = this.dispositionService.getNextDispositionAction(filePlanComponent); + if (da != null) + { + // Get the disposition event + if(parameters != null) + { + EventCompletionDetails event = getEvent(da, eventName); + if (event != null) + { + return true; + } + else + { + if (throwException) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_EVENT_NO_DISP_LC, eventName)); + } + else + { + return false; + } + } + } + else + { + return true; + } + } + } + return false; + } + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/CreateDispositionScheduleAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/CreateDispositionScheduleAction.java new file mode 100644 index 0000000000..f03a60cce7 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/CreateDispositionScheduleAction.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action.impl; + +import java.io.Serializable; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.repository.NodeRef; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Create disposition schedule action + * + * @author Roy Wetherall + */ +public class CreateDispositionScheduleAction extends RMActionExecuterAbstractBase +{ + /** Logger */ + @SuppressWarnings("unused") + private static Log logger = LogFactory.getLog(CreateDispositionScheduleAction.class); + + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + // Create the disposition schedule + dispositionService.createDispositionSchedule(actionedUponNodeRef, null); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase#isExecutableImpl(org.alfresco.service.cmr.repository.NodeRef, java.util.Map, boolean) + */ + @Override + public boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + boolean result = true; + if (recordsManagementService.isRecordCategory(filePlanComponent) == false) + { + if (throwException == true) + { + throw new AlfrescoRuntimeException("The disposition schedule could not be created, because the actioned upon node was not a record category."); + } + else + { + result = false; + } + } + + return result; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/CutOffAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/CutOffAction.java new file mode 100644 index 0000000000..299b48614c --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/CutOffAction.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action.impl; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.module.org_alfresco_module_rm.action.RMDispositionActionExecuterAbstractBase; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * Cut off disposition action + * + * @author Roy Wetherall + */ +public class CutOffAction extends RMDispositionActionExecuterAbstractBase +{ + private static final String MSG_ERR = "rm.action.close-record-folder-not-folder"; + + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RMDispositionActionExecuterAbstractBase#executeRecordFolderLevelDisposition(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeRecordFolderLevelDisposition(Action action, NodeRef recordFolder) + { + // Close folder + Boolean isClosed = (Boolean)this.nodeService.getProperty(recordFolder, PROP_IS_CLOSED); + if (Boolean.FALSE.equals(isClosed) == true) + { + this.nodeService.setProperty(recordFolder, PROP_IS_CLOSED, true); + } + + // Mark the folder as cut off + doCutOff(recordFolder); + + // Mark all the declared children of the folder as cut off + List records = this.recordsManagementService.getRecords(recordFolder); + for (NodeRef record : records) + { + doCutOff(record); + } + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RMDispositionActionExecuterAbstractBase#executeRecordLevelDisposition(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeRecordLevelDisposition(Action action, NodeRef record) + { + // Mark the record as cut off + doCutOff(record); + } + + /** + * Marks the record or record folder as cut off, calculating the cut off date. + * + * @param nodeRef node reference + */ + private void doCutOff(NodeRef nodeRef) + { + if (this.nodeService.hasAspect(nodeRef, ASPECT_CUT_OFF) == false) + { + // Apply the cut off aspect and set cut off date + Map cutOffProps = new HashMap(1); + cutOffProps.put(PROP_CUT_OFF_DATE, new Date()); + this.nodeService.addAspect(nodeRef, ASPECT_CUT_OFF, cutOffProps); + } + } + + @Override + public Set getProtectedProperties() + { + HashSet qnames = new HashSet(); + qnames.add(PROP_CUT_OFF_DATE); + return qnames; + } + + @Override + public Set getProtectedAspects() + { + HashSet qnames = new HashSet(); + qnames.add(ASPECT_CUT_OFF); + return qnames; + } + + @Override + protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + if(!super.isExecutableImpl(filePlanComponent, parameters, throwException)) + { + return false; + } + + // duplicates code from close .. it should get the closed action somehow? + if (this.recordsManagementService.isRecordFolder(filePlanComponent) + || this.recordsManagementService.isRecord(filePlanComponent)) + { + return true; + } + else + { + if (throwException) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_ERR, filePlanComponent.toString())); + } + else + { + return false; + } + } + } + + + } \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DeclareRecordAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DeclareRecordAction.java new file mode 100644 index 0000000000..d37078ad1c --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DeclareRecordAction.java @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action.impl; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.OwnableService; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * Declare record action + * + * @author Roy Wetherall + */ +public class DeclareRecordAction extends RMActionExecuterAbstractBase +{ + /** I18N */ + private static final String MSG_UNDECLARED_ONLY_RECORDS = "rm.action.undeclared-only-records"; + private static final String MSG_NO_DECLARE_MAND_PROP = "rm.action.no-declare-mand-prop"; + + /** Logger */ + private static Log logger = LogFactory.getLog(DeclareRecordAction.class); + + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, + * org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + if (recordsManagementService.isRecord(actionedUponNodeRef) == true) + { + if (recordsManagementService.isRecordDeclared(actionedUponNodeRef) == false) + { + List missingProperties = new ArrayList(5); + // Aspect not already defined - check mandatory properties then add + if (mandatoryPropertiesSet(actionedUponNodeRef, missingProperties) == true) + { + // Add the declared aspect + Map declaredProps = new HashMap(2); + declaredProps.put(PROP_DECLARED_AT, new Date()); + declaredProps.put(PROP_DECLARED_BY, AuthenticationUtil.getRunAsUser()); + this.nodeService.addAspect(actionedUponNodeRef, ASPECT_DECLARED_RECORD, declaredProps); + + // remove all owner related rights + this.ownableService.setOwner(actionedUponNodeRef, OwnableService.NO_OWNER); + } + else + { + throw new AlfrescoRuntimeException(buildMissingPropertiesErrorString(missingProperties)); + } + } + } + else + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_UNDECLARED_ONLY_RECORDS, actionedUponNodeRef.toString())); + } + } + + private String buildMissingPropertiesErrorString(List missingProperties) + { + StringBuilder builder = new StringBuilder(255); + builder.append(I18NUtil.getMessage(MSG_NO_DECLARE_MAND_PROP)); + builder.append(" "); + for (String missingProperty : missingProperties) + { + builder.append(missingProperty) + .append(", "); + } + return builder.toString(); + } + + /** + * Helper method to check whether all the mandatory properties of the node have been set + * + * @param nodeRef + * node reference + * @return boolean true if all mandatory properties are set, false otherwise + */ + private boolean mandatoryPropertiesSet(NodeRef nodeRef, List missingProperties) + { + boolean result = true; + + Map nodeRefProps = this.nodeService.getProperties(nodeRef); + + QName nodeRefType = this.nodeService.getType(nodeRef); + + TypeDefinition typeDef = this.dictionaryService.getType(nodeRefType); + for (PropertyDefinition propDef : typeDef.getProperties().values()) + { + if (propDef.isMandatory() == true) + { + if (nodeRefProps.get(propDef.getName()) == null) + { + logMissingProperty(propDef, missingProperties); + + result = false; + break; + } + } + } + + if (result != false) + { + Set aspects = this.nodeService.getAspects(nodeRef); + for (QName aspect : aspects) + { + AspectDefinition aspectDef = this.dictionaryService.getAspect(aspect); + for (PropertyDefinition propDef : aspectDef.getProperties().values()) + { + if (propDef.isMandatory() == true) + { + if (nodeRefProps.get(propDef.getName()) == null) + { + logMissingProperty(propDef, missingProperties); + + result = false; + break; + } + } + } + } + } + + return result; + } + + /** + * Log information about missing properties. + * + * @param propDef property definition + * @param missingProperties missing properties + */ + private void logMissingProperty(PropertyDefinition propDef, List missingProperties) + { + if (logger.isWarnEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Mandatory property missing: ").append(propDef.getName()); + logger.warn(msg.toString()); + } + missingProperties.add(propDef.getName().toString()); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase#getProtectedAspects() + */ + @Override + public Set getProtectedAspects() + { + HashSet qnames = new HashSet(); + qnames.add(ASPECT_DECLARED_RECORD); + return qnames; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase#isExecutableImpl(org.alfresco.service.cmr.repository.NodeRef, java.util.Map, boolean) + */ + @Override + protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + if (recordsManagementService.isRecord(filePlanComponent) == true) + { + if (recordsManagementService.isRecordDeclared(filePlanComponent) == false) + { + // Aspect not already defined - check mandatory properties then add + List missingProperties = new ArrayList(10); + if (mandatoryPropertiesSet(filePlanComponent, missingProperties) == true) + { + return true; + } + else + { + if (throwException) + { + throw new AlfrescoRuntimeException(buildMissingPropertiesErrorString(missingProperties)); + } + else + { + return false; + } + } + } + else + { + return false; + } + } + else + { + if (throwException) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_UNDECLARED_ONLY_RECORDS, filePlanComponent.toString())); + } + else + { + return false; + } + } + } + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DestroyAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DestroyAction.java new file mode 100644 index 0000000000..6d4e5b8a82 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DestroyAction.java @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action.impl; + +import java.io.Serializable; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.model.RenditionModel; +import org.alfresco.module.org_alfresco_module_rm.action.RMDispositionActionExecuterAbstractBase; +import org.alfresco.repo.content.ContentServicePolicies; +import org.alfresco.repo.content.cleanup.EagerContentStoreCleaner; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * Destroy action. + * + * @author Roy Wetherall + */ +public class DestroyAction extends RMDispositionActionExecuterAbstractBase + implements ContentServicePolicies.OnContentUpdatePolicy, + InitializingBean +{ + /** I18N */ + private static final String MSG_GHOSTED_PROP_UPDATE = "rm.action.ghosted-prop-update"; + + /** Policy component */ + private PolicyComponent policyComponent; + + /** Eager content store cleaner */ + private EagerContentStoreCleaner eagerContentStoreCleaner; + + /** Indicates if ghosting is enabled or not */ + private boolean ghostingEnabled = true; + + /** + * @param policyComponent policy component + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * @param eagerContentStoreCleaner eager content store cleaner + */ + public void setEagerContentStoreCleaner(EagerContentStoreCleaner eagerContentStoreCleaner) + { + this.eagerContentStoreCleaner = eagerContentStoreCleaner; + } + + /** + * @param ghostingEnabled true if ghosting is enabled, false otherwise + */ + public void setGhostingEnabled(boolean ghostingEnabled) + { + this.ghostingEnabled = ghostingEnabled; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RMDispositionActionExecuterAbstractBase#executeRecordFolderLevelDisposition(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeRecordFolderLevelDisposition(Action action, NodeRef recordFolder) + { + List records = this.recordsManagementService.getRecords(recordFolder); + for (NodeRef record : records) + { + executeRecordLevelDisposition(action, record); + } + + if (ghostingEnabled == true) + { + nodeService.addAspect(recordFolder, ASPECT_GHOSTED, Collections. emptyMap()); + } + else + { + nodeService.deleteNode(recordFolder); + } + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RMDispositionActionExecuterAbstractBase#executeRecordLevelDisposition(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeRecordLevelDisposition(Action action, NodeRef record) + { + doDestroy(record); + } + + /** + * Do the content destroy + * + * @param nodeRef + */ + private void doDestroy(NodeRef nodeRef) + { + // Clear the content + clearAllContent(nodeRef); + + // Clear thumbnail content + clearThumbnails(nodeRef); + + if (ghostingEnabled == true) + { + // Add the ghosted aspect + nodeService.addAspect(nodeRef, ASPECT_GHOSTED, null); + } + else + { + // If ghosting is not enabled, delete the node + nodeService.deleteNode(nodeRef); + } + } + + /** + * Clear all the content properties + * + * @param nodeRef + */ + private void clearAllContent(NodeRef nodeRef) + { + Set props = this.nodeService.getProperties(nodeRef).keySet(); + props.retainAll(this.dictionaryService.getAllProperties(DataTypeDefinition.CONTENT)); + for (QName prop : props) + { + // Clear the content + clearContent(nodeRef, prop); + + // Remove the property + this.nodeService.removeProperty(nodeRef, prop); + } + } + + /** + * Clear all the thumbnail information + * + * @param nodeRef + */ + @SuppressWarnings("deprecation") + private void clearThumbnails(NodeRef nodeRef) + { + // Remove the renditioned aspect (and its properties and associations) if it is present. + // + // From Alfresco 3.3 it is the rn:renditioned aspect which defines the + // child-association being considered in this method. + // Note also that the cm:thumbnailed aspect extends the rn:renditioned aspect. + // + // We want to remove the rn:renditioned aspect, but due to the possibility + // that there is Alfresco 3.2-era data with the cm:thumbnailed aspect + // applied, we must consider removing it too. + if (nodeService.hasAspect(nodeRef, RenditionModel.ASPECT_RENDITIONED) || + nodeService.hasAspect(nodeRef, ContentModel.ASPECT_THUMBNAILED)) + { + // Add the ghosted aspect to all the renditioned children, so that they will not be archived when the + // renditioned aspect is removed + Set childAssocTypes = dictionaryService.getAspect(RenditionModel.ASPECT_RENDITIONED).getChildAssociations().keySet(); + for (ChildAssociationRef child : nodeService.getChildAssocs(nodeRef)) + { + if (childAssocTypes.contains(child.getTypeQName())) + { + // Clear the content and delete the rendition + clearAllContent(child.getChildRef()); + nodeService.deleteNode(child.getChildRef()); + } + } + } + } + + /** + * Clear a content property + * + * @param nodeRef + * @param contentProperty + */ + private void clearContent(NodeRef nodeRef, QName contentProperty) + { + // Ensure the content is cleaned at the end of the transaction + ContentData contentData = (ContentData)nodeService.getProperty(nodeRef, contentProperty); + if (contentData != null && contentData.getContentUrl() != null) + { + eagerContentStoreCleaner.registerOrphanedContentUrl(contentData.getContentUrl(), true); + } + } + + /** + * @see org.alfresco.repo.content.ContentServicePolicies.OnContentUpdatePolicy#onContentUpdate(org.alfresco.service.cmr.repository.NodeRef, boolean) + */ + public void onContentUpdate(NodeRef nodeRef, boolean newContent) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_GHOSTED_PROP_UPDATE)); + } + + /** + * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() + */ + public void afterPropertiesSet() throws Exception + { + // Register interest in the onContentUpdate policy + policyComponent.bindClassBehaviour( + ContentServicePolicies.OnContentUpdatePolicy.QNAME, + ASPECT_GHOSTED, + new JavaBehaviour(this, "onContentUpdate")); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/EditDispositionActionAsOfDateAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/EditDispositionActionAsOfDateAction.java new file mode 100644 index 0000000000..f1a8e45609 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/EditDispositionActionAsOfDateAction.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action.impl; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * Edit review as of date action. + * + * @author Roy Wetherall + */ +public class EditDispositionActionAsOfDateAction extends RMActionExecuterAbstractBase +{ + /** I18N */ + private static final String MSG_VALID_DATE_DISP_ASOF = "rm.action.valid-date-disp-asof"; + private static final String MSG_DISP_ASOF_LIFECYCLE_APPLIED = "rm.action.disp-asof-lifecycle-applied"; + + /** Logger */ + @SuppressWarnings("unused") + private static Log logger = LogFactory.getLog(EditDispositionActionAsOfDateAction.class); + + /** Action parameters */ + public static final String PARAM_AS_OF_DATE = "asOfDate"; + + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, + * org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + if (this.nodeService.hasAspect(actionedUponNodeRef, ASPECT_DISPOSITION_LIFECYCLE) == true) + { + // Get the action parameter + Date asOfDate = (Date)action.getParameterValue(PARAM_AS_OF_DATE); + if (asOfDate == null) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_VALID_DATE_DISP_ASOF)); + } + + // Set the dispostion action as of date + DispositionAction da = dispositionService.getNextDispositionAction(actionedUponNodeRef); + if (da != null) + { + nodeService.setProperty(da.getNodeRef(), PROP_DISPOSITION_AS_OF, asOfDate); + } + } + } + + /** + * @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List) + */ + @Override + protected void addParameterDefinitions(List paramList) + { + // Intentionally empty + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase#getProtectedProperties() + */ + @Override + public Set getProtectedProperties() + { + HashSet qnames = new HashSet(); + qnames.add(PROP_DISPOSITION_AS_OF); + return qnames; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase#isExecutableImpl(org.alfresco.service.cmr.repository.NodeRef, java.util.Map, boolean) + */ + @Override + protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + boolean result = false; + if (this.nodeService.hasAspect(filePlanComponent, ASPECT_DISPOSITION_LIFECYCLE) == true) + { + result = true; + } + else + { + if (throwException == true) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_DISP_ASOF_LIFECYCLE_APPLIED)); + } + } + return result; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/EditHoldReasonAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/EditHoldReasonAction.java new file mode 100644 index 0000000000..f840f8e42f --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/EditHoldReasonAction.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action.impl; + +import java.io.Serializable; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * Edit freeze reason Action + * + * @author Roy Wetherall + */ +public class EditHoldReasonAction extends RMActionExecuterAbstractBase +{ + private static final String MSG_HOLD_EDIT_REASON_NONE = "rm.action.hold-edit-reason-none"; + private static final String MSG_HOLD_EDIT_TYPE = "rm.action.hold-edit-type"; + + /** Parameter names */ + public static final String PARAM_REASON = "reason"; + + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + QName nodeType = this.nodeService.getType(actionedUponNodeRef); + if (this.dictionaryService.isSubClass(nodeType, TYPE_HOLD) == true) + { + // Get the property values + String reason = (String)action.getParameterValue(PARAM_REASON); + if (reason == null || reason.length() == 0) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_HOLD_EDIT_REASON_NONE)); + } + + // Set the hold reason + nodeService.setProperty(actionedUponNodeRef, PROP_HOLD_REASON, reason); + + } + else + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_HOLD_EDIT_TYPE, TYPE_HOLD.toString(), actionedUponNodeRef.toString())); + } + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase#getProtectedAspects() + */ + @Override + public Set getProtectedAspects() + { + HashSet qnames = new HashSet(); + qnames.add(ASPECT_FROZEN); + return qnames; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase#getProtectedProperties() + */ + @Override + public Set getProtectedProperties() + { + HashSet qnames = new HashSet(); + qnames.add(PROP_HOLD_REASON); + return qnames; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase#isExecutableImpl(org.alfresco.service.cmr.repository.NodeRef, java.util.Map, boolean) + */ + @Override + protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + QName nodeType = this.nodeService.getType(filePlanComponent); + if (this.dictionaryService.isSubClass(nodeType, TYPE_HOLD) == true) + { + return true; + } + else + { + if(throwException) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_HOLD_EDIT_TYPE, TYPE_HOLD.toString(), filePlanComponent.toString())); + } + else + { + return false; + } + } + } + + +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/EditReviewAsOfDateAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/EditReviewAsOfDateAction.java new file mode 100644 index 0000000000..114ba6eb79 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/EditReviewAsOfDateAction.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action.impl; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * + * Edit review as of date action + * + * @author Roy Wetherall + */ +public class EditReviewAsOfDateAction extends RMActionExecuterAbstractBase +{ + /** I18N */ + private static final String MSG_SPECIFY_VALID_DATE = "rm.action.specify-avlid-date"; + private static final String MSG_REVIEW_DETAILS_ONLY = "rm.action.review-details-only"; + + @SuppressWarnings("unused") + private static Log logger = LogFactory.getLog(EditReviewAsOfDateAction.class); + + public static final String PARAM_AS_OF_DATE = "asOfDate"; + + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, + * org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + if (recordsManagementService.isRecord(actionedUponNodeRef) == true && + this.nodeService.hasAspect(actionedUponNodeRef, ASPECT_VITAL_RECORD) == true) + { + // Get the action parameter + Date reviewAsOf = (Date)action.getParameterValue(PARAM_AS_OF_DATE); + if (reviewAsOf == null) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_SPECIFY_VALID_DATE)); + } + + // Set the as of date + this.nodeService.setProperty(actionedUponNodeRef, PROP_REVIEW_AS_OF, reviewAsOf); + + } + } + + /** + * + * @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List) + */ + @Override + protected void addParameterDefinitions(List paramList) + { + // Intentionally empty + } + + @Override + public Set getProtectedProperties() + { + HashSet qnames = new HashSet(); + qnames.add(PROP_REVIEW_AS_OF); + return qnames; + } + + @Override + protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + boolean result = false; + if (recordsManagementService.isRecord(filePlanComponent) == true && + this.nodeService.hasAspect(filePlanComponent, ASPECT_VITAL_RECORD) == true) + { + result = true; + } + else + { + if (throwException == true) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_REVIEW_DETAILS_ONLY)); + } + } + return result; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/FileAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/FileAction.java new file mode 100644 index 0000000000..5a0e0cc058 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/FileAction.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action.impl; + +import java.io.Serializable; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordDefinition; +import org.alfresco.repo.action.ParameterDefinitionImpl; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * Files a record into a particular record folder + * + * @author Roy Wetherall + */ +public class FileAction extends RMActionExecuterAbstractBase +{ + /** Parameter names */ + public static final String PARAM_RECORD_METADATA_ASPECTS = "recordMetadataAspects"; + + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @SuppressWarnings("unchecked") + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + // Permissions perform the following checks so this action doesn't need to. + // + // check the record is within a folder + // check that the folder we are filing into is not closed + + // Get the optional list of record meta-data aspects + List recordMetadataAspects = (List)action.getParameterValue(PARAM_RECORD_METADATA_ASPECTS); + + // Add the record aspect (doesn't matter if it is already present) + if (nodeService.hasAspect(actionedUponNodeRef, ASPECT_RECORD) == false) + { + nodeService.addAspect(actionedUponNodeRef, RecordsManagementModel.ASPECT_RECORD, null); + } + + // Get the records properties + Map recordProperties = this.nodeService.getProperties(actionedUponNodeRef); + + Calendar fileCalendar = Calendar.getInstance(); + if (recordProperties.get(RecordsManagementModel.PROP_IDENTIFIER) == null) + { + // Calculate the filed date and record identifier + String year = Integer.toString(fileCalendar.get(Calendar.YEAR)); + QName nodeDbid = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "node-dbid"); + String recordId = year + "-" + padString(recordProperties.get(nodeDbid).toString(), 10); + recordProperties.put(RecordsManagementModel.PROP_IDENTIFIER, recordId); + } + + // Update/set the date this record was refiled/filed + recordProperties.put(RecordsManagementModel.PROP_DATE_FILED, fileCalendar.getTime()); + + // Set the record properties + this.nodeService.setProperties(actionedUponNodeRef, recordProperties); + + // Apply any record meta-data aspects + if (recordMetadataAspects != null && recordMetadataAspects.size() != 0) + { + for (QName aspect : recordMetadataAspects) + { + nodeService.addAspect(actionedUponNodeRef, aspect, null); + } + } + + // Calculate the review schedule + VitalRecordDefinition viDef = vitalRecordService.getVitalRecordDefinition(actionedUponNodeRef); + if (viDef != null && viDef.isEnabled() == true) + { + Date reviewAsOf = viDef.getNextReviewDate(); + if (reviewAsOf != null) + { + Map reviewProps = new HashMap(1); + reviewProps.put(RecordsManagementModel.PROP_REVIEW_AS_OF, reviewAsOf); + + if (!nodeService.hasAspect(actionedUponNodeRef, ASPECT_VITAL_RECORD)) + { + this.nodeService.addAspect(actionedUponNodeRef, RecordsManagementModel.ASPECT_VITAL_RECORD, reviewProps); + } + else + { + Map props = nodeService.getProperties(actionedUponNodeRef); + props.putAll(reviewProps); + nodeService.setProperties(actionedUponNodeRef, props); + } + } + } + + // Get the disposition instructions for the actioned upon record + DispositionSchedule di = this.dispositionService.getDispositionSchedule(actionedUponNodeRef); + + // Set up the disposition schedule if the dispositions are being managed at the record level + if (di != null && di.isRecordLevelDisposition() == true) + { + // Setup the next disposition action + updateNextDispositionAction(actionedUponNodeRef); + } + } + + /** + * @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List) + */ + @Override + protected void addParameterDefinitions(List paramList) + { + // No parameters + paramList.add(new ParameterDefinitionImpl(PARAM_RECORD_METADATA_ASPECTS, DataTypeDefinition.QNAME, false, "Record Metadata Aspects", true)); + } + + @Override + public Set getProtectedAspects() + { + HashSet qnames = new HashSet(); + qnames.add(ASPECT_RECORD); + qnames.add(ASPECT_VITAL_RECORD); + return qnames; + } + + @Override + public Set getProtectedProperties() + { + HashSet qnames = new HashSet(); + qnames.add(PROP_DATE_FILED); + qnames.add(PROP_REVIEW_AS_OF); + return qnames; + } + + @Override + protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + return true; + } + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/FreezeAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/FreezeAction.java new file mode 100644 index 0000000000..2ceb3da826 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/FreezeAction.java @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action.impl; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * Freeze Action + * + * @author Roy Wetherall + */ +public class FreezeAction extends RMActionExecuterAbstractBase +{ + private static final String MSG_FREEZE_NO_REASON = "rm.action.freeze-no-reason"; + private static final String MSG_FREEZE_ONLY_RECORDS_FOLDERS = "rm.action.freeze-only-records-folders"; + + /** Logger */ + private static Log logger = LogFactory.getLog(FreezeAction.class); + + /** Parameter names */ + public static final String PARAM_REASON = "reason"; + + /** Hold node reference key */ + private static final String KEY_HOLD_NODEREF = "holdNodeRef"; + + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + final boolean isRecord = recordsManagementService.isRecord(actionedUponNodeRef); + final boolean isFolder = this.recordsManagementService.isRecordFolder(actionedUponNodeRef); + + if (isRecord || isFolder) + { + // Get the property values + String reason = (String)action.getParameterValue(PARAM_REASON); + if (reason == null || reason.length() == 0) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_FREEZE_NO_REASON)); + } + + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Freezing node ").append(actionedUponNodeRef); + if (isFolder) + { + msg.append(" (folder)"); + } + msg.append(" with reason '").append(reason).append("'"); + logger.debug(msg.toString()); + } + + // Get the root rm node + NodeRef root = this.recordsManagementService.getFilePlan(actionedUponNodeRef); + + // Get the hold object + NodeRef holdNodeRef = (NodeRef)AlfrescoTransactionSupport.getResource(KEY_HOLD_NODEREF); + + if (holdNodeRef == null) + { + // Calculate a transfer name + QName nodeDbid = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "node-dbid"); + Long dbId = (Long)this.nodeService.getProperty(actionedUponNodeRef, nodeDbid); + String transferName = padString(dbId.toString(), 10); + + // Create the hold object + Map holdProps = new HashMap(2); + holdProps.put(ContentModel.PROP_NAME, transferName); + holdProps.put(PROP_HOLD_REASON, reason); + final QName transferQName = QName.createQName(RM_URI, transferName); + holdNodeRef = this.nodeService.createNode(root, + ASSOC_HOLDS, + transferQName, + TYPE_HOLD, + holdProps).getChildRef(); + + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Created hold object ").append(holdNodeRef) + .append(" with transfer name ").append(transferQName); + logger.debug(msg.toString()); + } + + // Bind the hold node reference to the transaction + AlfrescoTransactionSupport.bindResource(KEY_HOLD_NODEREF, holdNodeRef); + } + + // Link the record to the hold + this.nodeService.addChild( holdNodeRef, + actionedUponNodeRef, + ASSOC_FROZEN_RECORDS, + ASSOC_FROZEN_RECORDS); + + // Apply the freeze aspect + Map props = new HashMap(2); + props.put(PROP_FROZEN_AT, new Date()); + props.put(PROP_FROZEN_BY, AuthenticationUtil.getFullyAuthenticatedUser()); + this.nodeService.addAspect(actionedUponNodeRef, ASPECT_FROZEN, props); + + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Frozen aspect applied to ").append(actionedUponNodeRef); + logger.debug(msg.toString()); + } + + + // Mark all the folders contents as frozen + if (isFolder) + { + List records = this.recordsManagementService.getRecords(actionedUponNodeRef); + for (NodeRef record : records) + { + this.nodeService.addAspect(record, ASPECT_FROZEN, props); + + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Frozen aspect applied to ").append(record); + logger.debug(msg.toString()); + } + } + } + } + else + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_FREEZE_ONLY_RECORDS_FOLDERS)); + } + } + + @Override + public Set getProtectedAspects() + { + HashSet qnames = new HashSet(); + qnames.add(ASPECT_FROZEN); + return qnames; + } + + @Override + public Set getProtectedProperties() + { + HashSet qnames = new HashSet(); + qnames.add(PROP_HOLD_REASON); + //TODO Add prop frozen at/by? + return qnames; + } + + @Override + protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + if (this.recordsManagementService.isRecord(filePlanComponent) == true || + this.recordsManagementService.isRecordFolder(filePlanComponent) == true) + { + // Get the property values + if(parameters != null) + { + String reason = (String)parameters.get(PARAM_REASON); + if (reason == null || reason.length() == 0) + { + if(throwException) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_FREEZE_NO_REASON)); + } + else + { + return false; + } + } + } + return true; + } + else + { + if(throwException) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_FREEZE_ONLY_RECORDS_FOLDERS)); + } + else + { + return false; + } + } + } + + +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/OpenRecordFolderAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/OpenRecordFolderAction.java new file mode 100644 index 0000000000..7cda1b9960 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/OpenRecordFolderAction.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action.impl; + +import java.io.Serializable; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * Action to re-open the records folder + * + * @author Roy Wetherall + */ +public class OpenRecordFolderAction extends RMActionExecuterAbstractBase +{ + private static final String MSG_NO_OPEN_RECORD_FOLDER = "rm.action.no-open-record-folder"; + + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, + * org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + // TODO check that the user in question has the correct permission to re-open a records folder + + if (this.recordsManagementService.isRecordFolder(actionedUponNodeRef) == true) + { + Boolean isClosed = (Boolean) this.nodeService.getProperty(actionedUponNodeRef, PROP_IS_CLOSED); + if (Boolean.TRUE.equals(isClosed) == true) + { + this.nodeService.setProperty(actionedUponNodeRef, PROP_IS_CLOSED, false); + } + } + else + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NO_OPEN_RECORD_FOLDER, actionedUponNodeRef.toString())); + } + } + + /** + * @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List) + */ + @Override + protected void addParameterDefinitions(List paramList) + { + // TODO Auto-generated method stub + } + + @Override + public Set getProtectedProperties() + { + HashSet qnames = new HashSet(); + qnames.add(PROP_IS_CLOSED); + return qnames; + } + + @Override + protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + if (this.recordsManagementService.isRecordFolder(filePlanComponent) == true) + { + return true; + } + else + { + if (throwException) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NO_OPEN_RECORD_FOLDER, filePlanComponent.toString())); + } + else + { + return false; + } + } + } + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/RelinquishHoldAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/RelinquishHoldAction.java new file mode 100644 index 0000000000..5fdb6a9676 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/RelinquishHoldAction.java @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action.impl; + +import java.io.Serializable; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * Relinquish Hold Action + * + * @author Roy Wetherall + */ +public class RelinquishHoldAction extends RMActionExecuterAbstractBase +{ + /** Logger */ + private static Log logger = LogFactory.getLog(RelinquishHoldAction.class); + + /** I18N */ + private static final String MSG_NOT_HOLD_TYPE = "rm.action.not-hold-type"; + + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + QName nodeType = this.nodeService.getType(actionedUponNodeRef); + if (this.dictionaryService.isSubClass(nodeType, TYPE_HOLD) == true) + { + final NodeRef holdBeingRelinquished = actionedUponNodeRef; + List frozenNodeAssocs = nodeService.getChildAssocs(holdBeingRelinquished, ASSOC_FROZEN_RECORDS, RegexQNamePattern.MATCH_ALL); + + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Relinquishing hold ").append(holdBeingRelinquished) + .append(" which has ").append(frozenNodeAssocs.size()).append(" frozen node(s)."); + logger.debug(msg.toString()); + } + + for (ChildAssociationRef assoc : frozenNodeAssocs) + { + final NodeRef nextFrozenNode = assoc.getChildRef(); + + // Remove the freeze if this is the only hold that references the node + removeFreeze(nextFrozenNode, holdBeingRelinquished); + } + + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Deleting hold object ").append(holdBeingRelinquished) + .append(" with name ").append(nodeService.getProperty(holdBeingRelinquished, ContentModel.PROP_NAME)); + logger.debug(msg.toString()); + } + + // Delete the hold node + this.nodeService.deleteNode(holdBeingRelinquished); + } + else + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NOT_HOLD_TYPE, TYPE_HOLD.toString(), actionedUponNodeRef.toString())); + } + } + + /** + * Removes a freeze from a node + * + * @param nodeRef node reference + */ + private void removeFreeze(NodeRef nodeRef, NodeRef holdBeingRelinquished) + { + // We should only remove the frozen aspect if there are no other 'holds' in effect for this node. + // One complication to consider is that holds can be placed on records or on folders. + // Therefore if the nodeRef here is a record, we need to go up the containment hierarchy looking + // for holds at each level. + + // Get all the holds and remove this node from them. + List parentAssocs = this.nodeService.getParentAssocs(nodeRef, ASSOC_FROZEN_RECORDS, RegexQNamePattern.MATCH_ALL); + // If the nodeRef is a record, there could also be applicable holds as parents of the folder(s). + if (recordsManagementService.isRecord(nodeRef)) + { + List parentFolders = recordsManagementService.getRecordFolders(nodeRef); + for (NodeRef folder : parentFolders) + { + List moreAssocs = nodeService.getParentAssocs(folder, ASSOC_FROZEN_RECORDS, RegexQNamePattern.MATCH_ALL); + parentAssocs.addAll(moreAssocs); + } + } + + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Removing freeze from ").append(nodeRef).append(" which has ") + .append(parentAssocs.size()).append(" holds"); + logger.debug(msg.toString()); + } + + boolean otherHoldsAreInEffect = false; + for (ChildAssociationRef chAssRef : parentAssocs) + { + if (!chAssRef.getParentRef().equals(holdBeingRelinquished)) + { + otherHoldsAreInEffect = true; + break; + } + } + + if (!otherHoldsAreInEffect) + { + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Removing frozen aspect from ").append(nodeRef); + logger.debug(msg.toString()); + } + + // Remove the aspect + this.nodeService.removeAspect(nodeRef, ASPECT_FROZEN); + } + + // Remove the freezes on the child records as long as there is no other hold referencing them + if (this.recordsManagementService.isRecordFolder(nodeRef) == true) + { + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append(nodeRef).append(" is a record folder"); + logger.debug(msg.toString()); + } + for (NodeRef record : recordsManagementService.getRecords(nodeRef)) + { + removeFreeze(record, holdBeingRelinquished); + } + } + + } + + @Override + public Set getProtectedAspects() + { + HashSet qnames = new HashSet(); + qnames.add(ASPECT_FROZEN); + return qnames; + } + + @Override + protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + QName nodeType = this.nodeService.getType(filePlanComponent); + if (this.dictionaryService.isSubClass(nodeType, TYPE_HOLD) == true) + { + return true; + } + else + { + if(throwException) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NOT_HOLD_TYPE, TYPE_HOLD.toString(), filePlanComponent.toString())); + } + else + { + return false; + } + } + } + + +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/RetainAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/RetainAction.java new file mode 100644 index 0000000000..f8577ff6fe --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/RetainAction.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action.impl; + +import org.alfresco.module.org_alfresco_module_rm.action.RMDispositionActionExecuterAbstractBase; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Retain action + * + * @author Roy Wetherall + */ +public class RetainAction extends RMDispositionActionExecuterAbstractBase +{ + @Override + protected void executeRecordFolderLevelDisposition(Action action, NodeRef recordFolder) + { + // Do nothing + } + + @Override + protected void executeRecordLevelDisposition(Action action, NodeRef record) + { + // Do nothing + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/SetupRecordFolderAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/SetupRecordFolderAction.java new file mode 100644 index 0000000000..052a379479 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/SetupRecordFolderAction.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action.impl; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; +import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordDefinition; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * Action to close the records folder + * + * @author Roy Wetherall + */ +public class SetupRecordFolderAction extends RMActionExecuterAbstractBase +{ + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + if (this.recordsManagementService.isRecordFolder(actionedUponNodeRef) == true) + { + // Inherit the vital record details + VitalRecordDefinition vrDef = vitalRecordService.getVitalRecordDefinition(actionedUponNodeRef); + Map props = new HashMap(2); + if (vrDef != null) + { + props.put(PROP_VITAL_RECORD_INDICATOR, vrDef.isEnabled()); + props.put(PROP_REVIEW_PERIOD, vrDef.getReviewPeriod()); + } + this.nodeService.addAspect(actionedUponNodeRef, ASPECT_VITAL_RECORD_DEFINITION, props); + + // Set up the disposition schedule if the dispositions are being managed at the folder level + DispositionSchedule di = this.dispositionService.getDispositionSchedule(actionedUponNodeRef); + if (di != null && di.isRecordLevelDisposition() == false) + { + // Setup the next disposition action + updateNextDispositionAction(actionedUponNodeRef); + } + } + } + + /** + * @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List) + */ + @Override + protected void addParameterDefinitions(List paramList) + { + } + + @Override + protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + return true; + } + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/SplitEmailAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/SplitEmailAction.java new file mode 100644 index 0000000000..74f6737eb7 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/SplitEmailAction.java @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action.impl; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.mail.MessagingException; +import javax.mail.Multipart; +import javax.mail.Part; +import javax.mail.internet.ContentType; +import javax.mail.internet.MimeMessage; +import javax.mail.internet.MimeUtility; +import javax.transaction.UserTransaction; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.model.ImapModel; +import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.surf.util.I18NUtil; +import org.springframework.util.FileCopyUtils; + +/** + * Split Email Action + * + * Splits the attachments for an email message out to independent records. + * + * @author Mark Rogers + */ +public class SplitEmailAction extends RMActionExecuterAbstractBase +{ + /** I18N */ + private static final String MSG_NO_READ_MIME_MESSAGE = "rm.action.no-read-mime-message"; + private static final String MSG_EMAIL_DECLARED = "rm.action.email-declared"; + private static final String MSG_EMAIL_NOT_RECORD = "rm.action.email-not-record"; + private static final String MSG_EMAIL_CREATE_CHILD_ASSOC = "rm.action.email-create-child-assoc"; + + /** Logger */ + private static Log logger = LogFactory.getLog(SplitEmailAction.class); + + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, + * org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + // get node type + nodeService.getType(actionedUponNodeRef); + + if (logger.isDebugEnabled() == true) + { + logger.debug("split email:" + actionedUponNodeRef); + } + + if (recordsManagementService.isRecord(actionedUponNodeRef) == true) + { + if (recordsManagementService.isRecordDeclared(actionedUponNodeRef) == false) + { + ChildAssociationRef parent = nodeService.getPrimaryParent(actionedUponNodeRef); + + /** + * Check whether the email message has already been split - do nothing if it has already been split + */ + List refs = nodeService.getTargetAssocs(actionedUponNodeRef, ImapModel.ASSOC_IMAP_ATTACHMENT); + if(refs.size() > 0) + { + if (logger.isDebugEnabled() == true) + { + logger.debug("mail message has already been split - do nothing"); + } + return; + } + + /** + * Get the content and if its a mime message then create atachments for each part + */ + try + { + ContentReader reader = contentService.getReader(actionedUponNodeRef, ContentModel.PROP_CONTENT); + InputStream is = reader.getContentInputStream(); + MimeMessage mimeMessage = new MimeMessage(null, is); + Object content = mimeMessage.getContent(); + if (content instanceof Multipart) + { + Multipart multipart = (Multipart)content; + + for (int i = 0, n = multipart.getCount(); i < n; i++) + { + Part part = multipart.getBodyPart(i); + if ("attachment".equalsIgnoreCase(part.getDisposition())) + { + createAttachment(actionedUponNodeRef, parent.getParentRef(), part); + } + } + } + } + catch (Exception e) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NO_READ_MIME_MESSAGE, e.toString()), e); + } + } + else + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_EMAIL_DECLARED, actionedUponNodeRef.toString())); + } + } + else + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_EMAIL_NOT_RECORD, actionedUponNodeRef.toString())); + } + } + + @Override + protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + if (recordsManagementService.isRecord(filePlanComponent) == true) + { + if (recordsManagementService.isRecordDeclared(filePlanComponent)) + { + if (throwException) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_EMAIL_DECLARED, filePlanComponent.toString())); + } + else + { + return false; + } + } + } + else + { + if (throwException) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_EMAIL_NOT_RECORD, filePlanComponent.toString())); + } + else + { + return false; + } + } + return true; + } + + /** + * Create attachment from Mime Message Part + * @param messageNodeRef - the node ref of the mime message + * @param parentNodeRef - the node ref of the parent folder + * @param part + * @throws MessagingException + * @throws IOException + */ + private void createAttachment(NodeRef messageNodeRef, NodeRef parentNodeRef, Part part) throws MessagingException, IOException + { + String fileName = part.getFileName(); + try + { + fileName = MimeUtility.decodeText(fileName); + } + catch (UnsupportedEncodingException e) + { + if (logger.isWarnEnabled()) + { + logger.warn("Cannot decode file name '" + fileName + "'", e); + } + } + + Map messageProperties = nodeService.getProperties(messageNodeRef); + String messageTitle = (String)messageProperties.get(ContentModel.PROP_NAME); + if(messageTitle == null) + { + messageTitle = fileName; + } + else + { + messageTitle = messageTitle + " - " + fileName; + } + + ContentType contentType = new ContentType(part.getContentType()); + + Map docProps = new HashMap(1); + docProps.put(ContentModel.PROP_NAME, messageTitle + " - " + fileName); + docProps.put(ContentModel.PROP_TITLE, fileName); + + /** + * Create an attachment node in the same folder as the message + */ + ChildAssociationRef attachmentRef = nodeService.createNode(parentNodeRef, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, fileName), + ContentModel.TYPE_CONTENT, + docProps); + + /** + * Write the content into the new attachment node + */ + ContentWriter writer = contentService.getWriter(attachmentRef.getChildRef(), ContentModel.PROP_CONTENT, true); + writer.setMimetype(contentType.getBaseType()); + OutputStream os = writer.getContentOutputStream(); + FileCopyUtils.copy(part.getInputStream(), os); + + /** + * Create a link from the message to the attachment + */ + createRMReference(messageNodeRef, attachmentRef.getChildRef()); + + + } + + QName assocDef = null; + + /** + * Create a link from the message to the attachment + */ + private void createRMReference(NodeRef parentRef, NodeRef childRef) + { + String sourceId = "message"; + String targetId = "attachment"; + + String compoundId = recordsManagementAdminService.getCompoundIdFor(sourceId, targetId); + + Map refs = recordsManagementAdminService.getCustomReferenceDefinitions(); + for(QName name : refs.keySet()) + { + // TODO how to find assocDef? + // Refs seems to be null + } + + if(assocDef == null) + { + assocDef = createReference(); + } + + recordsManagementAdminService.addCustomReference(parentRef, childRef, assocDef); + + // add the IMAP attachment aspect + nodeService.createAssociation( + parentRef, + childRef, + ImapModel.ASSOC_IMAP_ATTACHMENT); + } + + /** + * Create the custom reference - need to jump through hoops with the transaction handling here + * since the association is created in the post commit phase, so it can't be used within the + * current transaction. + * + * @return + */ + private QName createReference() + { + UserTransaction txn = null; + + try + { + txn = transactionService.getNonPropagatingUserTransaction(); + txn.begin(); + RetryingTransactionHelper helper = transactionService.getRetryingTransactionHelper(); + RetryingTransactionCallback addCustomChildAssocDefinitionCallback = new RetryingTransactionCallback() + { + public QName execute() throws Throwable + { + String sourceId = "message"; + String targetId = "attachment"; + QName assocDef = recordsManagementAdminService.addCustomChildAssocDefinition(sourceId, targetId); + return assocDef; + } + }; + QName ret = helper.doInTransaction(addCustomChildAssocDefinitionCallback); + + txn.commit(); + return ret; + } + catch (Exception e) + { + if(txn != null) + { + try + { + txn.rollback(); + } + catch (Exception se) + { + logger.error("error during creation of custom child association", se); + // we can do nothing with this rollback exception. + } + } + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_EMAIL_CREATE_CHILD_ASSOC), e); + } + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/TransferAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/TransferAction.java new file mode 100644 index 0000000000..ba051b93b6 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/TransferAction.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action.impl; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.action.RMDispositionActionExecuterAbstractBase; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition; +import org.alfresco.repo.action.executer.ActionExecuter; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * Transfer action + * + * @author Roy Wetherall + */ +public class TransferAction extends RMDispositionActionExecuterAbstractBase +{ + /** Transfer node reference key */ + public static final String KEY_TRANSFER_NODEREF = "transferNodeRef"; + + /** I18N */ + public static final String MSG_NODE_ALREADY_TRANSFER = "rm.action.node-already-transfer"; + + /** Indicates whether the transfer is an accession or not */ + private boolean isAccession = false; + + /** + * Indicates whether this transfer is an accession or not + * + * @param isAccession + */ + public void setIsAccession(boolean isAccession) + { + this.isAccession = isAccession; + } + + /** + * Do not set the transfer action to auto-complete + * + * @see org.alfresco.module.org_alfresco_module_rm.action.RMDispositionActionExecuterAbstractBase#getSetDispositionActionComplete() + */ + @Override + public boolean getSetDispositionActionComplete() + { + return false; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RMDispositionActionExecuterAbstractBase#executeRecordFolderLevelDisposition(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeRecordFolderLevelDisposition(Action action, NodeRef recordFolder) + { + doTransfer(action, recordFolder); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RMDispositionActionExecuterAbstractBase#executeRecordLevelDisposition(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeRecordLevelDisposition(Action action, NodeRef record) + { + doTransfer(action, record); + } + + /** + * Create the transfer node and link the disposition lifecycle node beneath it + * + * @param dispositionLifeCycleNodeRef disposition lifecycle node + */ + private void doTransfer(Action action, NodeRef dispositionLifeCycleNodeRef) + { + // Get the root rm node + NodeRef root = this.recordsManagementService.getFilePlan(dispositionLifeCycleNodeRef); + + // Get the transfer object + NodeRef transferNodeRef = (NodeRef)AlfrescoTransactionSupport.getResource(KEY_TRANSFER_NODEREF); + if (transferNodeRef == null) + { + // Calculate a transfer name + QName nodeDbid = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "node-dbid"); + Long dbId = (Long)this.nodeService.getProperty(dispositionLifeCycleNodeRef, nodeDbid); + String transferName = padString(dbId.toString(), 10); + + // Create the transfer object + Map transferProps = new HashMap(2); + transferProps.put(ContentModel.PROP_NAME, transferName); + transferProps.put(PROP_TRANSFER_ACCESSION_INDICATOR, this.isAccession); + + // setup location property from disposition schedule + DispositionAction da = dispositionService.getNextDispositionAction(dispositionLifeCycleNodeRef); + if (da != null) + { + DispositionActionDefinition actionDef = da.getDispositionActionDefinition(); + if (actionDef != null) + { + transferProps.put(PROP_TRANSFER_LOCATION, actionDef.getLocation()); + } + } + + transferNodeRef = this.nodeService.createNode(root, + ASSOC_TRANSFERS, + QName.createQName(RM_URI, transferName), + TYPE_TRANSFER, + transferProps).getChildRef(); + + // Bind the hold node reference to the transaction + AlfrescoTransactionSupport.bindResource(KEY_TRANSFER_NODEREF, transferNodeRef); + } + + // Link the record to the trasnfer object + this.nodeService.addChild(transferNodeRef, + dispositionLifeCycleNodeRef, + ASSOC_TRANSFERRED, + ASSOC_TRANSFERRED); + + // Set PDF indicator flag + setPDFIndicationFlag(transferNodeRef, dispositionLifeCycleNodeRef); + + // Set the transferring indicator aspect + nodeService.addAspect(dispositionLifeCycleNodeRef, ASPECT_TRANSFERRING, null); + + // Set the return value of the action + action.setParameterValue(ActionExecuter.PARAM_RESULT, transferNodeRef); + } + + /** + * + * @param transferNodeRef + * @param dispositionLifeCycleNodeRef + */ + private void setPDFIndicationFlag(NodeRef transferNodeRef, NodeRef dispositionLifeCycleNodeRef) + { + if (recordsManagementService.isRecordFolder(dispositionLifeCycleNodeRef) == true) + { + List records = recordsManagementService.getRecords(dispositionLifeCycleNodeRef); + for (NodeRef record : records) + { + setPDFIndicationFlag(transferNodeRef, record); + } + } + else + { + ContentData contentData = (ContentData)nodeService.getProperty(dispositionLifeCycleNodeRef, ContentModel.PROP_CONTENT); + if (contentData != null && + MimetypeMap.MIMETYPE_PDF.equals(contentData.getMimetype()) == true) + { + // Set the property indicator + nodeService.setProperty(transferNodeRef, PROP_TRANSFER_PDF_INDICATOR, true); + } + } + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RMDispositionActionExecuterAbstractBase#isExecutableImpl(org.alfresco.service.cmr.repository.NodeRef, java.util.Map, boolean) + */ + @Override + protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + + if(!super.isExecutableImpl(filePlanComponent, parameters, throwException)) + { + // super will throw ... + return false; + } + NodeRef transferNodeRef = (NodeRef)AlfrescoTransactionSupport.getResource(KEY_TRANSFER_NODEREF); + if (transferNodeRef != null) + { + List transferredAlready = nodeService.getChildAssocs(transferNodeRef, ASSOC_TRANSFERRED, ASSOC_TRANSFERRED); + for(ChildAssociationRef car : transferredAlready) + { + if(car.getChildRef().equals(filePlanComponent)) + { + if (throwException) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NODE_ALREADY_TRANSFER, filePlanComponent.toString())); + } + else + { + return false; + } + } + } + } + return true; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/TransferCompleteAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/TransferCompleteAction.java new file mode 100644 index 0000000000..5ed098a921 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/TransferCompleteAction.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action.impl; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * Transfer complete action + * + * @author Roy Wetherall + */ +public class TransferCompleteAction extends RMActionExecuterAbstractBase +{ + /** I18N */ + private static final String MSG_NODE_NOT_TRANSFER = "rm.action.node-not-transfer"; + + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase#isExecutableImpl(org.alfresco.service.cmr.repository.NodeRef, + * java.util.Map, boolean) + */ + @Override + protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + QName className = this.nodeService.getType(filePlanComponent); + if (this.dictionaryService.isSubClass(className, TYPE_TRANSFER) == true) + { + return true; + } + else + { + List assocs = this.nodeService.getParentAssocs(filePlanComponent, ASSOC_TRANSFERRED, RegexQNamePattern.MATCH_ALL); + return assocs.size() > 0; + } + } + + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, + * org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + QName className = this.nodeService.getType(actionedUponNodeRef); + if (this.dictionaryService.isSubClass(className, TYPE_TRANSFER) == true) + { + boolean accessionIndicator = ((Boolean)nodeService.getProperty(actionedUponNodeRef, PROP_TRANSFER_ACCESSION_INDICATOR)).booleanValue(); + + List assocs = this.nodeService.getChildAssocs(actionedUponNodeRef, ASSOC_TRANSFERRED, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef assoc : assocs) + { + markComplete(assoc.getChildRef(), accessionIndicator); + } + + // Delete the transfer object + this.nodeService.deleteNode(actionedUponNodeRef); + + NodeRef transferNodeRef = (NodeRef) AlfrescoTransactionSupport.getResource(TransferAction.KEY_TRANSFER_NODEREF); + if (transferNodeRef != null) + { + if (transferNodeRef.equals(actionedUponNodeRef)) + { + AlfrescoTransactionSupport.bindResource(TransferAction.KEY_TRANSFER_NODEREF, null); + } + } + } + else + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NODE_NOT_TRANSFER)); + } + } + + /** + * Marks the node complete + * + * @param nodeRef + * disposition lifecycle node reference + */ + private void markComplete(NodeRef nodeRef, boolean accessionIndicator) + { + // Set the completed date + DispositionAction da = dispositionService.getNextDispositionAction(nodeRef); + if (da != null) + { + nodeService.setProperty(da.getNodeRef(), PROP_DISPOSITION_ACTION_COMPLETED_AT, new Date()); + nodeService.setProperty(da.getNodeRef(), PROP_DISPOSITION_ACTION_COMPLETED_BY, AuthenticationUtil.getRunAsUser()); + } + + // Remove the transferring indicator aspect + nodeService.removeAspect(nodeRef, ASPECT_TRANSFERRING); + + // Determine which marker aspect to use + QName markerAspectQName = null; + if (accessionIndicator == true) + { + markerAspectQName = ASPECT_ASCENDED; + } + else + { + markerAspectQName = ASPECT_TRANSFERRED; + } + + // Mark the object and children accordingly + nodeService.addAspect(nodeRef, markerAspectQName, null); + if (recordsManagementService.isRecordFolder(nodeRef) == true) + { + List records = recordsManagementService.getRecords(nodeRef); + for (NodeRef record : records) + { + nodeService.addAspect(record, markerAspectQName, null); + } + } + + // Update to the next disposition action + updateNextDispositionAction(nodeRef); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/UnCutoffAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/UnCutoffAction.java new file mode 100644 index 0000000000..69eb983ada --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/UnCutoffAction.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action.impl; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.repository.NodeRef; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * UnCutoff action implementation + * + * @author Roy Wetherall + */ +public class UnCutoffAction extends RMActionExecuterAbstractBase +{ + /** I18N */ + private static final String MSG_UNDO_NOT_LAST = "rm.action.undo-not-last"; + + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + if (nodeService.hasAspect(actionedUponNodeRef, ASPECT_DISPOSITION_LIFECYCLE) == true && + nodeService.hasAspect(actionedUponNodeRef, ASPECT_CUT_OFF) == true) + { + // Get the last disposition action + DispositionAction da = dispositionService.getLastCompletedDispostionAction(actionedUponNodeRef); + + // Check that the last disposition action was a cutoff + if (da == null || da.getName().equals("cutoff") == false) + { + // Can not undo cut off since cut off was not the last thing done + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_UNDO_NOT_LAST)); + } + + // Delete the current disposition action + DispositionAction currentDa = dispositionService.getNextDispositionAction(actionedUponNodeRef); + if (currentDa != null) + { + nodeService.deleteNode(currentDa.getNodeRef()); + } + + // Move the previous (cutoff) disposition back to be current + nodeService.moveNode(da.getNodeRef(), actionedUponNodeRef, ASSOC_NEXT_DISPOSITION_ACTION, ASSOC_NEXT_DISPOSITION_ACTION); + + // Reset the started and completed property values + nodeService.setProperty(da.getNodeRef(), PROP_DISPOSITION_ACTION_STARTED_AT, null); + nodeService.setProperty(da.getNodeRef(), PROP_DISPOSITION_ACTION_STARTED_BY, null); + nodeService.setProperty(da.getNodeRef(), PROP_DISPOSITION_ACTION_COMPLETED_AT, null); + nodeService.setProperty(da.getNodeRef(), PROP_DISPOSITION_ACTION_COMPLETED_BY, null); + + // Remove the cutoff aspect + nodeService.removeAspect(actionedUponNodeRef, ASPECT_CUT_OFF); + if (recordsManagementService.isRecordFolder(actionedUponNodeRef) == true) + { + List records = this.recordsManagementService.getRecords(actionedUponNodeRef); + for (NodeRef record : records) + { + nodeService.removeAspect(record, ASPECT_CUT_OFF); + } + } + } + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase#isExecutableImpl(org.alfresco.service.cmr.repository.NodeRef, java.util.Map, boolean) + */ + @Override + protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + boolean result = true; + + if (nodeService.hasAspect(filePlanComponent, ASPECT_DISPOSITION_LIFECYCLE) == true && + nodeService.hasAspect(filePlanComponent, ASPECT_CUT_OFF) == true) + { + // Get the last disposition action + DispositionAction da = dispositionService.getLastCompletedDispostionAction(filePlanComponent); + + // Check that the last disposition action was a cutoff + if (da == null || da.getName().equals("cutoff") == false) + { + if (throwException == true) + { + // Can not undo cut off since cut off was not the last thing done + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_UNDO_NOT_LAST)); + } + result = false; + } + } + else + { + if (throwException == true) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_UNDO_NOT_LAST)); + } + result = false; + } + + return result; + } + + +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/UndeclareRecordAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/UndeclareRecordAction.java new file mode 100644 index 0000000000..a2a926f22d --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/UndeclareRecordAction.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action.impl; + +import java.io.Serializable; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * Undeclare record action + * + * @author Roy Wetherall + */ +public class UndeclareRecordAction extends RMActionExecuterAbstractBase +{ + /** I18N */ + private static final String MSG_RECORDS_ONLY_UNDECLARED = "rm.action.records_only_undeclared"; + + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + if (recordsManagementService.isRecord(actionedUponNodeRef) == true) + { + if (recordsManagementService.isRecordDeclared(actionedUponNodeRef) == true) + { + // Remove the declared aspect + this.nodeService.removeAspect(actionedUponNodeRef, ASPECT_DECLARED_RECORD); + } + } + else + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_RECORDS_ONLY_UNDECLARED)); + } + } + + @Override + public Set getProtectedAspects() + { + HashSet qnames = new HashSet(); + qnames.add(ASPECT_DECLARED_RECORD); + return qnames; + } + + @Override + protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + if (recordsManagementService.isRecord(filePlanComponent) == true) + { + if (recordsManagementService.isRecordDeclared(filePlanComponent) == true) + { + return true; + } + return false; + } + else + { + if(throwException) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_RECORDS_ONLY_UNDECLARED)); + } + else + { + return false; + } + } + } + + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/UndoEventAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/UndoEventAction.java new file mode 100644 index 0000000000..571fe8d159 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/UndoEventAction.java @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action.impl; + +import java.io.Serializable; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction; +import org.alfresco.module.org_alfresco_module_rm.event.EventCompletionDetails; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * Undo event action + * + * @author Roy Wetherall + */ +public class UndoEventAction extends RMActionExecuterAbstractBase +{ + /** I18N */ + private static final String MSG_EVENT_NOT_DONE = "rm.action.event-not-undone"; + + /** Params */ + public static final String PARAM_EVENT_NAME = "eventName"; + + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + String eventName = (String)action.getParameterValue(PARAM_EVENT_NAME); + + if (this.nodeService.hasAspect(actionedUponNodeRef, ASPECT_DISPOSITION_LIFECYCLE) == true) + { + // Get the next disposition action + DispositionAction da = this.dispositionService.getNextDispositionAction(actionedUponNodeRef); + if (da != null) + { + // Get the disposition event + EventCompletionDetails event = getEvent(da, eventName); + if (event != null) + { + // Update the event so that it is undone + NodeRef eventNodeRef = event.getNodeRef(); + Map props = this.nodeService.getProperties(eventNodeRef); + props.put(PROP_EVENT_EXECUTION_COMPLETE, false); + props.put(PROP_EVENT_EXECUTION_COMPLETED_AT, null); + props.put(PROP_EVENT_EXECUTION_COMPLETED_BY, null); + this.nodeService.setProperties(eventNodeRef, props); + + // Check to see if the events eligible property needs to be updated + updateEventEigible(da); + + } + else + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_EVENT_NOT_DONE, eventName)); + } + } + } + } + + /** + * Get the event from the dispostion action + * + * @param da + * @param eventName + * @return + */ + private EventCompletionDetails getEvent(DispositionAction da, String eventName) + { + EventCompletionDetails result = null; + List events = da.getEventCompletionDetails(); + for (EventCompletionDetails event : events) + { + if (eventName.equals(event.getEventName()) == true) + { + result = event; + break; + } + } + return result; + } + + /** + * + * @param da + * @param nodeRef + */ + private void updateEventEigible(DispositionAction da) + { + List events = da.getEventCompletionDetails(); + + boolean eligible = false; + if (da.getDispositionActionDefinition().eligibleOnFirstCompleteEvent() == false) + { + eligible = true; + for (EventCompletionDetails event : events) + { + if (event.isEventComplete() == false) + { + eligible = false; + break; + } + } + } + else + { + for (EventCompletionDetails event : events) + { + if (event.isEventComplete() == true) + { + eligible = true; + break; + } + } + } + + // Update the property with the eligible value + this.nodeService.setProperty(da.getNodeRef(), PROP_DISPOSITION_EVENTS_ELIGIBLE, eligible); + } + + + /** + * @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List) + */ + @Override + protected void addParameterDefinitions(List paramList) + { + // TODO add parameter definitions .... + // eventName + + } + + @Override + public Set getProtectedAspects() + { + HashSet qnames = new HashSet(); + qnames.add(ASPECT_DISPOSITION_LIFECYCLE); + return qnames; + } + + @Override + public Set getProtectedProperties() + { + HashSet qnames = new HashSet(); + qnames.add(PROP_EVENT_EXECUTION_COMPLETE); + qnames.add(PROP_EVENT_EXECUTION_COMPLETED_AT); + qnames.add(PROP_EVENT_EXECUTION_COMPLETED_BY); + return qnames; + } + + @Override + protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + String eventName = null; + if(parameters != null) + { + eventName = (String)parameters.get(PARAM_EVENT_NAME); + } + if (this.nodeService.hasAspect(filePlanComponent, ASPECT_DISPOSITION_LIFECYCLE) == true) + { + // Get the next disposition action + DispositionAction da = this.dispositionService.getNextDispositionAction(filePlanComponent); + if (da != null) + { + // Get the disposition event + if(parameters != null) + { + EventCompletionDetails event = getEvent(da, eventName); + if (event != null) + { + return true; + } + else + { + if(throwException) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_EVENT_NOT_DONE, eventName)); + } + } + } + else + { + return true; + } + } + } + return false; + } + + + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/UnfreezeAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/UnfreezeAction.java new file mode 100644 index 0000000000..9189184f75 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/UnfreezeAction.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action.impl; + +import java.io.Serializable; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Unfreeze Action + * + * @author Roy Wetherall + */ +public class UnfreezeAction extends RMActionExecuterAbstractBase +{ + /** Logger */ + private static Log logger = LogFactory.getLog(UnfreezeAction.class); + + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, + * org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + if (this.nodeService.hasAspect(actionedUponNodeRef, ASPECT_FROZEN) == true) + { + final boolean isFolder = this.recordsManagementService.isRecordFolder(actionedUponNodeRef); + + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Unfreezing node ").append(actionedUponNodeRef); + if (isFolder) + { + msg.append(" (folder)"); + } + logger.debug(msg.toString()); + } + + // Remove freeze from node + removeFreeze(actionedUponNodeRef); + + // Remove freeze from records if a record folder + if (isFolder) + { + List records = this.recordsManagementService.getRecords(actionedUponNodeRef); + for (NodeRef record : records) + { + removeFreeze(record); + } + } + } + } + + /** + * Removes a freeze from a node + * + * @param nodeRef + * node reference + */ + private void removeFreeze(NodeRef nodeRef) + { + // Get all the holds and remove this node from them + List assocs = this.nodeService.getParentAssocs(nodeRef, ASSOC_FROZEN_RECORDS, RegexQNamePattern.MATCH_ALL); + + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Removing freeze from node ").append(nodeRef) + .append("which has ").append(assocs.size()).append(" holds"); + logger.debug(msg.toString()); + } + + for (ChildAssociationRef assoc : assocs) + { + // Remove the frozen node as a child + NodeRef holdNodeRef = assoc.getParentRef(); + this.nodeService.removeChild(holdNodeRef, nodeRef); + + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Removed frozen node from hold ").append(holdNodeRef); + logger.debug(msg.toString()); + } + + // Check to see if we should delete the hold + List holdAssocs = this.nodeService.getChildAssocs(holdNodeRef, ASSOC_FROZEN_RECORDS, RegexQNamePattern.MATCH_ALL); + if (holdAssocs.size() == 0) + { + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Hold node ").append(holdNodeRef) + .append(" with name ").append(nodeService.getProperty(holdNodeRef, ContentModel.PROP_NAME)) + .append(" has no frozen nodes. Hence deleting it."); + logger.debug(msg.toString()); + } + + // Delete the hold object + this.nodeService.deleteNode(holdNodeRef); + } + } + + // Remove the aspect + this.nodeService.removeAspect(nodeRef, ASPECT_FROZEN); + + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Removed frozen aspect from ").append(nodeRef); + logger.debug(msg.toString()); + } + } + + @Override + public Set getProtectedAspects() + { + HashSet qnames = new HashSet(); + qnames.add(ASPECT_FROZEN); + return qnames; + } + + @Override + protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + return this.nodeService.hasAspect(filePlanComponent, ASPECT_FROZEN); + } + +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/AuditEvent.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/AuditEvent.java new file mode 100644 index 0000000000..0cd942fd5c --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/AuditEvent.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.audit; + +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * Class to represent an audit event + * + * @author Gavin Cornwell + */ +public class AuditEvent +{ + private final String name; + private final String label; + + /** + * Constructor + * + * @param name The audit event name + * @param label The audit event label (or I18N lookup id) + */ + public AuditEvent(String name, String label) + { + this.name = name; + + String lookup = I18NUtil.getMessage(label); + if (lookup != null) + { + label = lookup; + } + this.label = label; + } + + /** + * + * @return The audit event name + */ + public String getName() + { + return this.name; + } + + /** + * + * @return The audit event label + */ + public String getLabel() + { + return this.label; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/AuthenticatedUserRolesDataExtractor.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/AuthenticatedUserRolesDataExtractor.java new file mode 100644 index 0000000000..abd6fc71b4 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/AuthenticatedUserRolesDataExtractor.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.audit; + +import java.io.Serializable; +import java.util.Set; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService; +import org.alfresco.module.org_alfresco_module_rm.security.Role; +import org.alfresco.repo.audit.extractor.AbstractDataExtractor; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; + +/** + * An extractor that uses a node context to determine the currently-authenticated + * user's RM roles. This is not a data generator because it can only function in + * the context of a give node. + * + * @author Derek Hulley + * @since 3.2 + */ +public final class AuthenticatedUserRolesDataExtractor extends AbstractDataExtractor +{ + private NodeService nodeService; + private RecordsManagementService rmService; + private RecordsManagementSecurityService rmSecurityService; + + /** + * Used to check that the node in the context is a fileplan component + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Used to find the RM root + */ + public void setRmService(RecordsManagementService rmService) + { + this.rmService = rmService; + } + + /** + * Used to get roles + */ + public void setRmSecurityService(RecordsManagementSecurityService rmSecurityService) + { + this.rmSecurityService = rmSecurityService; + } + + /** + * @return Returns true if the data is a NodeRef and it represents + * a fileplan component + */ + public boolean isSupported(Serializable data) + { + if (data == null || !(data instanceof NodeRef)) + { + return false; + } + return nodeService.hasAspect((NodeRef)data, RecordsManagementModel.ASPECT_FILE_PLAN_COMPONENT); + } + + public Serializable extractData(Serializable value) throws Throwable + { + NodeRef nodeRef = (NodeRef) value; + String user = AuthenticationUtil.getFullyAuthenticatedUser(); + if (user == null) + { + // No-one is authenticated + return null; + } + + // Get the rm root + NodeRef rmRootNodeRef = rmService.getFilePlan(nodeRef); + + Set roles = rmSecurityService.getRolesByUser(rmRootNodeRef, user); + StringBuilder sb = new StringBuilder(100); + for (Role role : roles) + { + if (sb.length() > 0) + { + sb.append(", "); + } + sb.append(role.getDisplayLabel()); + } + + // Done + return sb.toString(); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/FilePlanIdentifierDataExtractor.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/FilePlanIdentifierDataExtractor.java new file mode 100644 index 0000000000..28faaa9905 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/FilePlanIdentifierDataExtractor.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.audit; + +import java.io.Serializable; + +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.audit.extractor.AbstractDataExtractor; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; + +/** + * An extractor that gets a node's {@link RecordsManagementModel#PROP_IDENTIFIER identifier} property. + * This will only extract data if the node is a + * {@link RecordsManagementModel#ASPECT_RECORD_COMPONENT_ID Record component identifier}. + * + * @author Derek Hulley + * @since 3.2 + */ +public final class FilePlanIdentifierDataExtractor extends AbstractDataExtractor +{ + private NodeService nodeService; + + /** + * Used to check that the node in the context is a fileplan component + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @return Returns true if the data is a NodeRef and it represents + * a fileplan component + */ + public boolean isSupported(Serializable data) + { + if (data == null || !(data instanceof NodeRef)) + { + return false; + } + return nodeService.hasAspect((NodeRef)data, RecordsManagementModel.ASPECT_RECORD_COMPONENT_ID); + } + + public Serializable extractData(Serializable value) throws Throwable + { + NodeRef nodeRef = (NodeRef) value; + + String identifier = (String) nodeService.getProperty(nodeRef, RecordsManagementModel.PROP_IDENTIFIER); + + // Done + return identifier; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/FilePlanNamePathDataExtractor.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/FilePlanNamePathDataExtractor.java new file mode 100644 index 0000000000..edd08cc022 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/FilePlanNamePathDataExtractor.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.audit; + +import java.io.Serializable; +import java.util.List; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.audit.extractor.AbstractDataExtractor; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; + +/** + * An extractor that extracts the cm:name path from the RM root down to + * - and including - the node's own name. This will only extract data if the + * node is a {@link RecordsManagementModel#ASPECT_FILE_PLAN_COMPONENT fileplan component}. + * + * @see RecordsManagementService#getNodeRefPath(NodeRef) + * + * @author Derek Hulley + * @since 3.2 + */ +public final class FilePlanNamePathDataExtractor extends AbstractDataExtractor +{ + private NodeService nodeService; + private RecordsManagementService rmService; + + /** + * Used to check that the node in the context is a fileplan component + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Used to find the RM root + */ + public void setRmService(RecordsManagementService rmService) + { + this.rmService = rmService; + } + + /** + * @return Returns true if the data is a NodeRef and it represents + * a fileplan component + */ + public boolean isSupported(Serializable data) + { + if (data == null || !(data instanceof NodeRef)) + { + return false; + } + return nodeService.hasAspect((NodeRef)data, RecordsManagementModel.ASPECT_FILE_PLAN_COMPONENT); + } + + public Serializable extractData(Serializable value) throws Throwable + { + NodeRef nodeRef = (NodeRef) value; + + // Get path from the RM root + List nodeRefPath = rmService.getNodeRefPath(nodeRef); + + StringBuilder sb = new StringBuilder(128); + for (NodeRef pathNodeRef : nodeRefPath) + { + String name = (String)nodeService.getProperty(pathNodeRef, ContentModel.PROP_NAME); + sb.append("/").append(name); + } + + // Done + return sb.toString(); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/FilePlanNodeRefPathDataExtractor.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/FilePlanNodeRefPathDataExtractor.java new file mode 100644 index 0000000000..5248d6bb9a --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/FilePlanNodeRefPathDataExtractor.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.audit; + +import java.io.Serializable; +import java.util.List; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.audit.extractor.AbstractDataExtractor; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; + +/** + * An extractor that extracts the NodeRef path from the RM root down to + * - and including - the node itself. This will only extract data if the + * node is a {@link RecordsManagementModel#ASPECT_FILE_PLAN_COMPONENT fileplan component}. + * + * @see RecordsManagementService#getNodeRefPath(NodeRef) + * + * @author Derek Hulley + * @since 3.2 + */ +public final class FilePlanNodeRefPathDataExtractor extends AbstractDataExtractor +{ + private NodeService nodeService; + private RecordsManagementService rmService; + + /** + * Used to check that the node in the context is a fileplan component + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Used to find the RM root + */ + public void setRmService(RecordsManagementService rmService) + { + this.rmService = rmService; + } + + /** + * @return Returns true if the data is a NodeRef and it represents + * a fileplan component + */ + public boolean isSupported(Serializable data) + { + if (data == null || !(data instanceof NodeRef)) + { + return false; + } + return nodeService.hasAspect((NodeRef)data, RecordsManagementModel.ASPECT_FILE_PLAN_COMPONENT); + } + + public Serializable extractData(Serializable value) throws Throwable + { + NodeRef nodeRef = (NodeRef) value; + + // Get path from the RM root + List nodeRefPath = rmService.getNodeRefPath(nodeRef); + + // Done + return (Serializable) nodeRefPath; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditEntry.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditEntry.java new file mode 100644 index 0000000000..b46f3a628e --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditEntry.java @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.audit; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.springframework.extensions.surf.util.ISO8601DateFormat; +import org.alfresco.util.Pair; +import org.alfresco.util.ParameterCheck; + +/** + * Class to represent a Records Management audit entry. + * + * @author Gavin Cornwell + */ +public final class RecordsManagementAuditEntry +{ + private final Date timestamp; + private final String userName; + private final String fullName; + private final String userRole; + private final NodeRef nodeRef; + private final String nodeName; + private final String nodeType; + private final String event; + private final String identifier; + private final String path; + private final Map beforeProperties; + private final Map afterProperties; + private Map> changedProperties; + + /** + * Default constructor + */ + public RecordsManagementAuditEntry(Date timestamp, + String userName, String fullName, String userRole, + NodeRef nodeRef, String nodeName, String nodeType, + String event, String identifier, String path, + Map beforeProperties, + Map afterProperties) + { + ParameterCheck.mandatory("timestamp", timestamp); + ParameterCheck.mandatory("userName", userName); + + this.timestamp = timestamp; + this.userName = userName; + this.userRole = userRole; + this.fullName = fullName; + this.nodeRef = nodeRef; + this.nodeName = nodeName; + this.nodeType = nodeType; + this.event = event; + this.identifier = identifier; + this.path = path; + this.beforeProperties = beforeProperties; + this.afterProperties = afterProperties; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("(") + .append("timestamp=").append(timestamp) + .append(", userName=").append(userName) + .append(", userRole=").append(userRole) + .append(", fullName=").append(fullName) + .append(", nodeRef=").append(nodeRef) + .append(", nodeName=").append(nodeName) + .append(", event=").append(event) + .append(", identifier=").append(identifier) + .append(", path=").append(path) + .append(", beforeProperties=").append(beforeProperties) + .append(", afterProperties=").append(afterProperties) + .append(", changedProperties=").append(changedProperties) + .append(")"); + return sb.toString(); + } + + /** + * + * @return The date of the audit entry + */ + public Date getTimestamp() + { + return this.timestamp; + } + + /** + * + * @return The date of the audit entry as an ISO8601 formatted String + */ + public String getTimestampString() + { + return ISO8601DateFormat.format(this.timestamp); + } + + /** + * + * @return The username of the user that caused the audit log entry to be created + */ + public String getUserName() + { + return this.userName; + } + + /** + * + * @return The full name of the user that caused the audit log entry to be created + */ + public String getFullName() + { + return this.fullName; + } + + /** + * + * @return The role of the user that caused the audit log entry to be created + */ + public String getUserRole() + { + return this.userRole; + } + + /** + * + * @return The NodeRef of the node the audit log entry is for + */ + public NodeRef getNodeRef() + { + return this.nodeRef; + } + + /** + * + * @return The name of the node the audit log entry is for + */ + public String getNodeName() + { + return this.nodeName; + } + + /** + * + * @return The type of the node the audit log entry is for + */ + public String getNodeType() + { + return this.nodeType; + } + + /** + * + * @return The human readable description of the reason for the audit log + * entry i.e. metadata updated, record declared + */ + public String getEvent() + { + return this.event; + } + + /** + * An identifier for the item being audited, for example for a record + * it will be the unique record identifier, for a user it would be the + * username etc. + * + * @return Ad identifier for the thing being audited + */ + public String getIdentifier() + { + return this.identifier; + } + + /** + * + * @return The path to the object being audited + */ + public String getPath() + { + return this.path; + } + + /** + * + * @return Map of properties before the audited action + */ + public Map getBeforeProperties() + { + return this.beforeProperties; + } + + /** + * + * @return Map of properties after the audited action + */ + public Map getAfterProperties() + { + return this.beforeProperties; + } + + /** + * + * @return Map of changed properties + */ + public Map> getChangedProperties() + { + if (this.changedProperties == null) + { + initChangedProperties(); + } + + return this.changedProperties; + } + + /** + * Initialises the map of changed values given the before and after properties + */ + private void initChangedProperties() + { + if (this.beforeProperties != null && this.afterProperties != null) + { + this.changedProperties = new HashMap>( + this.beforeProperties.size() + this.afterProperties.size()); + + // add all the properties present before the audited action + for (QName valuePropName : this.beforeProperties.keySet()) + { + Pair values = new Pair( + this.beforeProperties.get(valuePropName), + this.afterProperties.get(valuePropName)); + this.changedProperties.put(valuePropName, values); + } + + // add all the properties present after the audited action that + // have not already been added + for (QName valuePropName : this.afterProperties.keySet()) + { + if (!this.beforeProperties.containsKey(valuePropName)) + { + Pair values = new Pair(null, + this.afterProperties.get(valuePropName)); + this.changedProperties.put(valuePropName, values); + } + } + } + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditQueryParameters.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditQueryParameters.java new file mode 100644 index 0000000000..98c1618ec7 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditQueryParameters.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.audit; + +import java.util.Date; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * Class to represent the parameters for a Records Management + * audit log query. + * + * @author Gavin Cornwell + */ +public final class RecordsManagementAuditQueryParameters +{ + private int maxEntries = -1; + private String user; + private NodeRef nodeRef; + private Date dateFrom; + private Date dateTo; + private String event; + private QName property; + + /** + * Default constructor. + */ + public RecordsManagementAuditQueryParameters() + { + } + + /** + * + * @return The username to filter by + */ + public String getUser() + { + return this.user; + } + + /** + * Restricts the retrieved audit trail to entries made by + * the provided user. + * + * @param user The username to filter by + */ + public void setUser(String user) + { + this.user = user; + } + + /** + * + * @return The maximum number of audit log entries to retrieve + */ + public int getMaxEntries() + { + return this.maxEntries; + } + + /** + * Restricts the retrieved audit trail to the last + * maxEntries entries. + * + * @param maxEntries Maximum number of entries + */ + public void setMaxEntries(int maxEntries) + { + this.maxEntries = maxEntries; + } + + /** + * + * @return The node to get entries for + */ + public NodeRef getNodeRef() + { + return this.nodeRef; + } + + /** + * Restricts the retrieved audit trail to only those entries + * created by the give node. + * + * @param nodeRef The node to get entries for + */ + public void setNodeRef(NodeRef nodeRef) + { + this.nodeRef = nodeRef; + } + + /** + * + * @return The date to retrieve entries from + */ + public Date getDateFrom() + { + return this.dateFrom; + } + + /** + * Restricts the retrieved audit trail to only those entries + * that occurred after the given date. + * + * @param dateFrom Date to retrieve entries after + */ + public void setDateFrom(Date dateFrom) + { + this.dateFrom = dateFrom; + } + + /** + * + * @return The date to retrive entries to + */ + public Date getDateTo() + { + return this.dateTo; + } + + /** + * Restricts the retrieved audit trail to only those entries + * that occurred before the given date. + * + * @param dateTo Date to retrieve entries before + */ + public void setDateTo(Date dateTo) + { + this.dateTo = dateTo; + } + + /** + * + * @return The event to retrive entries for + */ + public String getEvent() + { + return this.event; + } + + /** + * Restricts the retrieved audit trail to only those entries + * that match the given event string. + * + * @param event Event to retrieve entries for + */ + public void setEvent(String event) + { + this.event = event; + } + + /** + * + * @return The property to retrieve entries for + */ + public QName getProperty() + { + return this.property; + } + + /** + * Restricts the audit trail to only those entries that involve + * the given property. + * + * @param property The property to retrieve entries for + */ + public void setProperty(QName property) + { + this.property = property; + } + + /* + * @see java.lang.Object#toString() + */ + @Override + public String toString() + { + StringBuilder builder = new StringBuilder(super.toString()); + + builder.append(" (nodeRef='").append(nodeRef).append("', user='") + .append(user).append("', dateFrom='").append(dateFrom) + .append("', dateTo='").append(dateTo).append("', maxEntries='") + .append(maxEntries).append("', event='").append(event) + .append("', property='").append(property).append("')"); + + return builder.toString(); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditService.java new file mode 100644 index 0000000000..f493d4949c --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditService.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.audit; + +import java.io.File; +import java.io.Serializable; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Records management audit service. + * + * @author Gavin Cornwell + */ +public interface RecordsManagementAuditService +{ + public enum ReportFormat { HTML, JSON } + + public static final String RM_AUDIT_EVENT_UPDATE_RM_OBJECT = "Update RM Object"; + public static final String RM_AUDIT_EVENT_CREATE_RM_OBJECT = "Create RM Object"; + public static final String RM_AUDIT_EVENT_DELETE_RM_OBJECT = "Delete RM Object"; + public static final String RM_AUDIT_EVENT_LOGIN_SUCCESS = "Login.Success"; + public static final String RM_AUDIT_EVENT_LOGIN_FAILURE = "Login.Failure"; + + public static final String RM_AUDIT_APPLICATION_NAME = "RM"; + public static final String RM_AUDIT_PATH_ROOT = "/RM"; + public static final String RM_AUDIT_SNIPPET_EVENT = "/event"; + public static final String RM_AUDIT_SNIPPET_PERSON = "/person"; + public static final String RM_AUDIT_SNIPPET_NAME = "/name"; + public static final String RM_AUDIT_SNIPPET_NODE = "/node"; + public static final String RM_AUDIT_SNIPPET_CHANGES = "/changes"; + public static final String RM_AUDIT_SNIPPET_BEFORE = "/before"; + public static final String RM_AUDIT_SNIPPET_AFTER = "/after"; + + public static final String RM_AUDIT_DATA_PERSON_FULLNAME = "/RM/event/person/fullName"; + public static final String RM_AUDIT_DATA_PERSON_ROLES = "/RM/event/person/roles"; + public static final String RM_AUDIT_DATA_EVENT_NAME = "/RM/event/name/value"; + public static final String RM_AUDIT_DATA_NODE_NODEREF = "/RM/event/node/noderef"; + public static final String RM_AUDIT_DATA_NODE_NAME = "/RM/event/node/name"; + public static final String RM_AUDIT_DATA_NODE_TYPE = "/RM/event/node/type"; + public static final String RM_AUDIT_DATA_NODE_IDENTIFIER = "/RM/event/node/identifier"; + public static final String RM_AUDIT_DATA_NODE_NAMEPATH = "/RM/event/node/namePath"; + public static final String RM_AUDIT_DATA_NODE_CHANGES_BEFORE = "/RM/event/node/changes/before/value"; + public static final String RM_AUDIT_DATA_NODE_CHANGES_AFTER = "/RM/event/node/changes/after/value"; + + public static final String RM_AUDIT_DATA_LOGIN_USERNAME = "/RM/login/args/userName/value"; + public static final String RM_AUDIT_DATA_LOGIN_FULLNAME = "/RM/login/no-error/fullName"; + public static final String RM_AUDIT_DATA_LOGIN_ERROR = "/RM/login/error/value"; + + /** + * Starts RM auditing. + */ + void start(); + + /** + * Stops RM auditing. + */ + void stop(); + + /** + * Clears the RM audit trail. + */ + void clear(); + + /** + * Determines whether the RM audit log is currently enabled. + * + * @return true if RM auditing is active false otherwise + */ + boolean isEnabled(); + + /** + * Returns the date the RM audit was last started. + * + * @return Date the audit was last started + */ + Date getDateLastStarted(); + + /** + * Returns the date the RM audit was last stopped. + * + * @return Date the audit was last stopped + */ + Date getDateLastStopped(); + + /** + * An explicit call that RM actions can make to have the events logged. + * + * @param action the action that will be performed + * @param nodeRef the component being acted on + * @param parameters the action's parameters + */ + void auditRMAction(RecordsManagementAction action, NodeRef nodeRef, Map parameters); + + /** + * Retrieves a list of audit log entries using the provided parameters + * represented by the RecordsManagementAuditQueryParameters instance. + *

+ * The parameters are all optional so an empty RecordsManagementAuditQueryParameters + * object will result in ALL audit log entries for the RM system being + * returned. Setting the various parameters effectively filters the full + * audit trail. + * + * @param params Parameters to use to retrieve audit trail (never null) + * @param format The format the report should be produced in + * @return File containing JSON representation of audit trail + */ + File getAuditTrailFile(RecordsManagementAuditQueryParameters params, ReportFormat format); + + /** + * Retrieves a list of audit log entries using the provided parameters + * represented by the RecordsManagementAuditQueryParameters instance. + *

+ * The parameters are all optional so an empty RecordsManagementAuditQueryParameters + * object will result in ALL audit log entries for the RM system being + * returned. Setting the various parameters effectively filters the full + * audit trail. + * + * @param params Parameters to use to retrieve audit trail (never null) + * @return All entries for the audit trail + */ + List getAuditTrail(RecordsManagementAuditQueryParameters params); + + /** + * Retrieves a list of audit log entries using the provided parameters + * represented by the RecordsManagementAuditQueryParameters instance and + * then files the resulting log as an undeclared record in the record folder + * represented by the given NodeRef. + *

+ * The parameters are all optional so an empty RecordsManagementAuditQueryParameters + * object will result in ALL audit log entries for the RM system being + * returned. Setting the various parameters effectively filters the full + * audit trail. + * + * @param params Parameters to use to retrieve audit trail (never null) + * @param destination NodeRef representing a record folder in which to file the audit log + * @param format The format the report should be produced in + * @return NodeRef of the undeclared record filed + */ + NodeRef fileAuditTrailAsRecord(RecordsManagementAuditQueryParameters params, + NodeRef destination, ReportFormat format); + + /** + * Retrieves a list of audit events. + * + * @return List of audit events + */ + List getAuditEvents(); +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditServiceImpl.java new file mode 100644 index 0000000000..70feec88db --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditServiceImpl.java @@ -0,0 +1,1326 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.audit; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Serializable; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.audit.AuditComponent; +import org.alfresco.repo.audit.model.AuditApplication; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy; +import org.alfresco.repo.node.NodeServicePolicies.OnCreateNodePolicy; +import org.alfresco.repo.node.NodeServicePolicies.OnUpdatePropertiesPolicy; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.TransactionListenerAdapter; +import org.alfresco.repo.transaction.TransactionalResourceHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.audit.AuditQueryParameters; +import org.alfresco.service.cmr.audit.AuditService; +import org.alfresco.service.cmr.audit.AuditService.AuditQueryCallback; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.Pair; +import org.alfresco.util.PropertyCheck; +import org.alfresco.util.PropertyMap; +import org.alfresco.util.TempFileProvider; +import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.springframework.context.ApplicationEvent; +import org.springframework.extensions.surf.util.AbstractLifecycleBean; +import org.springframework.extensions.surf.util.I18NUtil; +import org.springframework.extensions.surf.util.ISO8601DateFormat; +import org.springframework.extensions.surf.util.ParameterCheck; + +/** + * Records Management Audit Service Implementation. + * + * @author Gavin Cornwell + * @since 3.2 + */ +public class RecordsManagementAuditServiceImpl + extends AbstractLifecycleBean + implements RecordsManagementAuditService, + NodeServicePolicies.OnCreateNodePolicy, + NodeServicePolicies.BeforeDeleteNodePolicy, + NodeServicePolicies.OnUpdatePropertiesPolicy +{ + /** I18N */ + private static final String MSG_UPDATED_METADATA = "rm.audit.updated-metadata"; + private static final String MSG_CREATED_OBJECT = "rm.audit.created-object"; + private static final String MSG_DELETE_OBJECT = "rm.audit.delte-object"; + private static final String MSG_LOGIN_SUCCEEDED = "rm.audit.login-succeeded"; + private static final String MSG_LOGIN_FAILED = "rm.audit.login-failed"; + private static final String MSG_FILED_RECORD = "rm.audit.filed-record"; + private static final String MSG_REVIEWED = "rm.audit.reviewed"; + private static final String MSG_CUT_OFF = "rm.audit.cut-off"; + private static final String MSG_REVERSED_CUT_OFF = "rm.audit.reversed-cut-off"; + private static final String MSG_DESTROYED_ITEM = "rm.audit.destroyed-item"; + private static final String MSG_OPENED_RECORD_FOLDER = "rm.audit.opened-record-folder"; + private static final String MSG_CLOSED_RECORD_FOLDER = "rm.audit.closed-record-folder"; + private static final String MSG_SETUP_RECORD_FOLDER = "rm.audit.setup-recorder-folder"; + private static final String MSG_DECLARED_RECORD = "rm.audit.declared-record"; + private static final String MSG_UNDECLARED_RECORD = "rm.audit.undeclared-record"; + private static final String MSG_FROZE_ITEM = "rm.audit.froze-item"; + private static final String MSG_RELINQUISED_HOLD = "rm.audit.relinquised-hold"; + private static final String MSG_UPDATED_HOLD_REASON = "rm.audit.updated-hold-reason"; + private static final String MSG_UPDATED_REVIEW_AS_OF_DATE = "rm.audit.updated-review-as-of-date"; + private static final String MSG_UPDATED_DISPOSITION_AS_OF_DATE = "rm.audit.updated-disposition-as-of-date"; + private static final String MSG_UPDATED_VITAL_RECORD_DEFINITION = "rm.audit.updated-vital-record-definition"; + private static final String MSG_UPDATED_DISPOSITOIN_ACTION_DEFINITION = "rm.audit.updated-disposition-action-definition"; + private static final String MSG_COMPELTED_EVENT = "rm.audit.completed-event"; + private static final String MSG_REVERSED_COMPLETE_EVENT = "rm.audit.revered-complete-event"; + private static final String MSG_TRANSFERRED_ITEM = "rm.audit.transferred-item"; + private static final String MSG_COMPLETED_TRANSFER = "rm.audit.completed-transfer"; + private static final String MSG_ACCESSION = "rm.audit.accession"; + private static final String MSG_COMPLETED_ACCESSION = "rm.audit.copmleted-accession"; + private static final String MSG_SCANNED_RECORD = "rm.audit.scanned-record"; + private static final String MSG_PDF_RECORD = "rm.audit.pdf-record"; + private static final String MSG_PHOTO_RECORD = "rm.audit.photo-record"; + private static final String MSG_WEB_RECORD = "rm.audit.web-record"; + private static final String MSG_TRAIL_FILE_FAIL = "rm.audit.trail-file-fail"; + private static final String MSG_AUDIT_REPORT = "rm.audit.audit-report"; + + /** Logger */ + private static Log logger = LogFactory.getLog(RecordsManagementAuditServiceImpl.class); + + private static final String KEY_RM_AUDIT_NODE_RECORDS = "RMAUditNodeRecords"; + + protected static final String AUDIT_TRAIL_FILE_PREFIX = "audit_"; + protected static final String AUDIT_TRAIL_JSON_FILE_SUFFIX = ".json"; + protected static final String AUDIT_TRAIL_HTML_FILE_SUFFIX = ".html"; + protected static final String FILE_ACTION = "file"; + + private PolicyComponent policyComponent; + private DictionaryService dictionaryService; + private TransactionService transactionService; + private NodeService nodeService; + private ContentService contentService; + private AuditComponent auditComponent; + private AuditService auditService; + private RecordsManagementService rmService; + private RecordsManagementActionService rmActionService; + + private boolean shutdown = false; + + private RMAuditTxnListener txnListener; + private Map auditEvents; + + public RecordsManagementAuditServiceImpl() + { + } + + /** + * Set the component used to bind to behaviour callbacks + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * Provides user-readable names for types + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + /** + * Set the component used to start new transactions + */ + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } + + /** + * Sets the NodeService instance + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Sets the ContentService instance + */ + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + + /** + * The component to create audit events + */ + public void setAuditComponent(AuditComponent auditComponent) + { + this.auditComponent = auditComponent; + } + + /** + * Sets the AuditService instance + */ + public void setAuditService(AuditService auditService) + { + this.auditService = auditService; + } + + /** + * Set the RecordsManagementService + */ + public void setRecordsManagementService(RecordsManagementService rmService) + { + this.rmService = rmService; + } + + /** + * Sets the RecordsManagementActionService instance + */ + public void setRecordsManagementActionService(RecordsManagementActionService rmActionService) + { + this.rmActionService = rmActionService; + } + + /** + * Checks that all necessary properties have been set. + */ + public void init() + { + PropertyCheck.mandatory(this, "policyComponent", policyComponent); + PropertyCheck.mandatory(this, "transactionService", transactionService); + PropertyCheck.mandatory(this, "nodeService", nodeService); + PropertyCheck.mandatory(this, "contentService", contentService); + PropertyCheck.mandatory(this, "auditComponent", auditComponent); + PropertyCheck.mandatory(this, "auditService", auditService); + PropertyCheck.mandatory(this, "rmService", rmService); + PropertyCheck.mandatory(this, "rmActionService", rmActionService); + PropertyCheck.mandatory(this, "dictionaryService", dictionaryService); + + // setup the audit events map + initAuditEvents(); + } + + protected void initAuditEvents() + { + // TODO: make this map configurable and localisable. + this.auditEvents = new HashMap(32); + + this.auditEvents.put(RM_AUDIT_EVENT_UPDATE_RM_OBJECT, + new AuditEvent(RM_AUDIT_EVENT_UPDATE_RM_OBJECT, MSG_UPDATED_METADATA)); + this.auditEvents.put(RM_AUDIT_EVENT_CREATE_RM_OBJECT, + new AuditEvent(RM_AUDIT_EVENT_CREATE_RM_OBJECT, MSG_CREATED_OBJECT)); + this.auditEvents.put(RM_AUDIT_EVENT_DELETE_RM_OBJECT, + new AuditEvent(RM_AUDIT_EVENT_DELETE_RM_OBJECT, MSG_DELETE_OBJECT)); + this.auditEvents.put(RM_AUDIT_EVENT_LOGIN_SUCCESS, + new AuditEvent(RM_AUDIT_EVENT_LOGIN_SUCCESS, MSG_LOGIN_SUCCEEDED)); + this.auditEvents.put(RM_AUDIT_EVENT_LOGIN_FAILURE, + new AuditEvent(RM_AUDIT_EVENT_LOGIN_FAILURE, MSG_LOGIN_FAILED)); + + this.auditEvents.put("file", + new AuditEvent("file", MSG_FILED_RECORD)); + this.auditEvents.put("reviewed", + new AuditEvent("reviewed", MSG_REVIEWED)); + this.auditEvents.put("cutoff", + new AuditEvent("cutoff", MSG_CUT_OFF)); + this.auditEvents.put("unCutoff", + new AuditEvent("unCutoff", MSG_REVERSED_CUT_OFF)); + this.auditEvents.put("destroy", + new AuditEvent("destroy", MSG_DESTROYED_ITEM)); + this.auditEvents.put("openRecordFolder", + new AuditEvent("openRecordFolder", MSG_OPENED_RECORD_FOLDER)); + this.auditEvents.put("closeRecordFolder", + new AuditEvent("closeRecordFolder", MSG_CLOSED_RECORD_FOLDER)); + this.auditEvents.put("setupRecordFolder", + new AuditEvent("setupRecordFolder", MSG_SETUP_RECORD_FOLDER)); + this.auditEvents.put("declareRecord", + new AuditEvent("declareRecord", MSG_DECLARED_RECORD)); + this.auditEvents.put("undeclareRecord", + new AuditEvent("undeclareRecord", MSG_UNDECLARED_RECORD)); + this.auditEvents.put("freeze", + new AuditEvent("freeze", MSG_FROZE_ITEM)); + this.auditEvents.put("relinquishHold", + new AuditEvent("relinquishHold", MSG_RELINQUISED_HOLD)); + this.auditEvents.put("editHoldReason", + new AuditEvent("editHoldReason", MSG_UPDATED_HOLD_REASON)); + this.auditEvents.put("editReviewAsOfDate", + new AuditEvent("editReviewAsOfDate", MSG_UPDATED_REVIEW_AS_OF_DATE)); + this.auditEvents.put("editDispositionActionAsOfDate", + new AuditEvent("editDispositionActionAsOfDate", MSG_UPDATED_DISPOSITION_AS_OF_DATE)); + this.auditEvents.put("broadcastVitalRecordDefinition", + new AuditEvent("broadcastVitalRecordDefinition", MSG_UPDATED_VITAL_RECORD_DEFINITION)); + this.auditEvents.put("broadcastDispositionActionDefinitionUpdate", + new AuditEvent("broadcastDispositionActionDefinitionUpdate", MSG_UPDATED_DISPOSITOIN_ACTION_DEFINITION)); + this.auditEvents.put("completeEvent", + new AuditEvent("completeEvent", MSG_COMPELTED_EVENT)); + this.auditEvents.put("undoEvent", + new AuditEvent("undoEvent", MSG_REVERSED_COMPLETE_EVENT)); + this.auditEvents.put("transfer", + new AuditEvent("transfer", MSG_TRANSFERRED_ITEM)); + this.auditEvents.put("transferComplete", + new AuditEvent("transferComplete", MSG_COMPLETED_TRANSFER)); + this.auditEvents.put("accession", + new AuditEvent("accession", MSG_ACCESSION)); + this.auditEvents.put("accessionComplete", + new AuditEvent("accessionComplete", MSG_COMPLETED_ACCESSION)); + this.auditEvents.put("applyScannedRecord", + new AuditEvent("applyScannedRecord", MSG_SCANNED_RECORD)); + this.auditEvents.put("applyPdfRecord", + new AuditEvent("applyPdfRecord", MSG_PDF_RECORD)); + this.auditEvents.put("applyDigitalPhotographRecord", + new AuditEvent("applyDigitalPhotographRecord", MSG_PHOTO_RECORD)); + this.auditEvents.put("applyWebRecord", + new AuditEvent("applyWebRecord", MSG_WEB_RECORD)); + } + + @Override + protected void onBootstrap(ApplicationEvent event) + { + shutdown = false; + txnListener = new RMAuditTxnListener(); + // Register to listen for property changes to rma:record types + policyComponent.bindClassBehaviour( + OnUpdatePropertiesPolicy.QNAME, + RecordsManagementModel.ASPECT_RECORD_COMPONENT_ID, + new JavaBehaviour(this, "onUpdateProperties")); + policyComponent.bindClassBehaviour( + OnCreateNodePolicy.QNAME, + RecordsManagementModel.ASPECT_RECORD_COMPONENT_ID, + new JavaBehaviour(this, "onCreateNode")); + policyComponent.bindClassBehaviour( + BeforeDeleteNodePolicy.QNAME, + RecordsManagementModel.ASPECT_RECORD_COMPONENT_ID, + new JavaBehaviour(this, "beforeDeleteNode")); + } + + @Override + protected void onShutdown(ApplicationEvent event) + { + shutdown = true; + } + + /** + * {@inheritDoc} + */ + public boolean isEnabled() + { + return auditService.isAuditEnabled( + RecordsManagementAuditService.RM_AUDIT_APPLICATION_NAME, + RecordsManagementAuditService.RM_AUDIT_PATH_ROOT); + } + + /** + * {@inheritDoc} + */ + public void start() + { + auditService.enableAudit( + RecordsManagementAuditService.RM_AUDIT_APPLICATION_NAME, + RecordsManagementAuditService.RM_AUDIT_PATH_ROOT); + if (logger.isInfoEnabled()) + logger.info("Started Records Management auditing"); + } + + /** + * {@inheritDoc} + */ + public void stop() + { + auditService.disableAudit( + RecordsManagementAuditService.RM_AUDIT_APPLICATION_NAME, + RecordsManagementAuditService.RM_AUDIT_PATH_ROOT); + if (logger.isInfoEnabled()) + logger.info("Stopped Records Management auditing"); + } + + /** + * {@inheritDoc} + */ + public void clear() + { + auditService.clearAudit(RecordsManagementAuditService.RM_AUDIT_APPLICATION_NAME, null, null); + if (logger.isInfoEnabled()) + logger.debug("Records Management audit log has been cleared"); + } + + /** + * {@inheritDoc} + */ + public Date getDateLastStarted() + { + // TODO: return proper date, for now it's today's date + return new Date(); + } + + /** + * {@inheritDoc} + */ + public Date getDateLastStopped() + { + // TODO: return proper date, for now it's today's date + return new Date(); + } + + /** + * A class to carry audit information through the transaction. + * + * @author Derek Hulley + * @since 3.2 + */ + private static class RMAuditNode + { + private String eventName; + private Map nodePropertiesBefore; + private Map nodePropertiesAfter; + + private RMAuditNode() + { + } + + public String getEventName() + { + return eventName; + } + + public void setEventName(String eventName) + { + this.eventName = eventName; + } + + public Map getNodePropertiesBefore() + { + return nodePropertiesBefore; + } + + public void setNodePropertiesBefore(Map nodePropertiesBefore) + { + this.nodePropertiesBefore = nodePropertiesBefore; + } + + public Map getNodePropertiesAfter() + { + return nodePropertiesAfter; + } + + public void setNodePropertiesAfter(Map nodePropertiesAfter) + { + this.nodePropertiesAfter = nodePropertiesAfter; + } + } + + public void onUpdateProperties(NodeRef nodeRef, Map before, Map after) + { + auditRMEvent(nodeRef, RM_AUDIT_EVENT_UPDATE_RM_OBJECT, before, after); + } + + public void beforeDeleteNode(NodeRef nodeRef) + { + auditRMEvent(nodeRef, RM_AUDIT_EVENT_DELETE_RM_OBJECT, null, null); + } + + public void onCreateNode(ChildAssociationRef childAssocRef) + { + auditRMEvent(childAssocRef.getChildRef(), RM_AUDIT_EVENT_CREATE_RM_OBJECT, null, null); + } + + /** + * {@inheritDoc} + * @since 3.2 + */ + public void auditRMAction( + RecordsManagementAction action, + NodeRef nodeRef, + Map parameters) + { + auditRMEvent(nodeRef, action.getName(), null, null); + } + + /** + * Audit an event for a node + * + * @param nodeRef the node to which the event applies + * @param eventName the name of the event + * @param nodePropertiesBefore properties before the event (optional) + * @param nodePropertiesAfter properties after the event (optional) + */ + private void auditRMEvent( + NodeRef nodeRef, + String eventName, + Map nodePropertiesBefore, + Map nodePropertiesAfter) + { + // If we are deleting nodes, then we need to audit NOW + if (eventName.equals(RecordsManagementAuditService.RM_AUDIT_EVENT_DELETE_RM_OBJECT)) + { + // Deleted nodes will not be available at the end of the transaction. The data needs to + // be extracted now and the audit entry needs to be created now. + Map auditMap = new HashMap(13); + auditMap.put( + AuditApplication.buildPath( + RecordsManagementAuditService.RM_AUDIT_SNIPPET_EVENT, + RecordsManagementAuditService.RM_AUDIT_SNIPPET_NAME), + eventName); + // Action node + auditMap.put( + AuditApplication.buildPath( + RecordsManagementAuditService.RM_AUDIT_SNIPPET_EVENT, + RecordsManagementAuditService.RM_AUDIT_SNIPPET_NODE), + nodeRef); + auditMap = auditComponent.recordAuditValues(RecordsManagementAuditService.RM_AUDIT_PATH_ROOT, auditMap); + if (logger.isDebugEnabled()) + { + logger.debug("RM Audit: Audited node deletion: \n" + auditMap); + } + } + else + { + // Create an event for auditing post-commit + Map auditedNodes = TransactionalResourceHelper.getMap(KEY_RM_AUDIT_NODE_RECORDS); + RMAuditNode auditedNode = auditedNodes.get(nodeRef); + if (auditedNode == null) + { + auditedNode = new RMAuditNode(); + auditedNodes.put(nodeRef, auditedNode); + // Bind the listener to the txn. We could do it anywhere in the method, this position ensures + // that we avoid some rebinding of the listener + AlfrescoTransactionSupport.bindListener(txnListener); + } + // Only update the eventName if it has not already been done + if (auditedNode.getEventName() == null) + { + auditedNode.setEventName(eventName); + } + // Set the properties before the start if they are not already available + if (auditedNode.getNodePropertiesBefore() == null) + { + auditedNode.setNodePropertiesBefore(nodePropertiesBefore); + } + // Set the after values if they are provided. + // Overwrite as we assume that these represent the latest state of the node. + if (nodePropertiesAfter != null) + { + auditedNode.setNodePropertiesAfter(nodePropertiesAfter); + } + // That is it. The values are queued for the end of the transaction. + } + } + + /** + * A stateless transaction listener for RM auditing. This component picks up the data of + * modified nodes and generates the audit information. + *

+ * This class is not static so that the instances will have access to the action's implementation. + * + * @author Derek Hulley + * @since 3.2 + */ + private class RMAuditTxnListener extends TransactionListenerAdapter + { + private final Log logger = LogFactory.getLog(RecordsManagementAuditServiceImpl.class); + + /* + * Equality and hashcode generation are left unimplemented; we expect to only have a single + * instance of this class per action. + */ + + /** + * Get the action parameters from the transaction and audit them. + */ + @Override + public void afterCommit() + { + final Map auditedNodes = TransactionalResourceHelper.getMap(KEY_RM_AUDIT_NODE_RECORDS); + + // Start a *new* read-write transaction to audit in + RetryingTransactionCallback auditCallback = new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + auditInTxn(auditedNodes); + return null; + } + }; + transactionService.getRetryingTransactionHelper().doInTransaction(auditCallback, false, true); + } + + /** + * Do the actual auditing, assuming the presence of a viable transaction + * + * @param auditedNodes details of the nodes that were modified + */ + private void auditInTxn(Map auditedNodes) throws Throwable + { + // Go through all the audit information and audit it + boolean auditedSomething = false; // We rollback if nothing is audited + for (Map.Entry entry : auditedNodes.entrySet()) + { + NodeRef nodeRef = entry.getKey(); + + // If the node is gone, then do nothing + if (!nodeService.exists(nodeRef)) + { + continue; + } + + RMAuditNode auditedNode = entry.getValue(); + + Map auditMap = new HashMap(13); + // Action description + String eventName = auditedNode.getEventName(); + auditMap.put( + AuditApplication.buildPath( + RecordsManagementAuditService.RM_AUDIT_SNIPPET_EVENT, + RecordsManagementAuditService.RM_AUDIT_SNIPPET_NAME), + eventName); + // Action node + auditMap.put( + AuditApplication.buildPath( + RecordsManagementAuditService.RM_AUDIT_SNIPPET_EVENT, + RecordsManagementAuditService.RM_AUDIT_SNIPPET_NODE), + nodeRef); + // Property changes + Map propertiesBefore = auditedNode.getNodePropertiesBefore(); + Map propertiesAfter = auditedNode.getNodePropertiesAfter(); + Pair, Map> deltaPair = + PropertyMap.getBeforeAndAfterMapsForChanges(propertiesBefore, propertiesAfter); + auditMap.put( + AuditApplication.buildPath( + RecordsManagementAuditService.RM_AUDIT_SNIPPET_EVENT, + RecordsManagementAuditService.RM_AUDIT_SNIPPET_NODE, + RecordsManagementAuditService.RM_AUDIT_SNIPPET_CHANGES, + RecordsManagementAuditService.RM_AUDIT_SNIPPET_BEFORE), + (Serializable) deltaPair.getFirst()); + auditMap.put( + AuditApplication.buildPath( + RecordsManagementAuditService.RM_AUDIT_SNIPPET_EVENT, + RecordsManagementAuditService.RM_AUDIT_SNIPPET_NODE, + RecordsManagementAuditService.RM_AUDIT_SNIPPET_CHANGES, + RecordsManagementAuditService.RM_AUDIT_SNIPPET_AFTER), + (Serializable) deltaPair.getSecond()); + // Audit it + if (logger.isDebugEnabled()) + { + logger.debug("RM Audit: Auditing values: \n" + auditMap); + } + auditMap = auditComponent.recordAuditValues(RecordsManagementAuditService.RM_AUDIT_PATH_ROOT, auditMap); + if (auditMap.isEmpty()) + { + if (logger.isDebugEnabled()) + { + logger.debug("RM Audit: Nothing was audited."); + } + } + else + { + if (logger.isDebugEnabled()) + { + logger.debug("RM Audit: Audited values: \n" + auditMap); + } + // We must commit the transaction to get the values in + auditedSomething = true; + } + } + // Check if anything was audited + if (!auditedSomething) + { + // Nothing was audited, so do nothing + RetryingTransactionHelper.getActiveUserTransaction().setRollbackOnly(); + } + } + } + + /** + * {@inheritDoc} + */ + public File getAuditTrailFile(RecordsManagementAuditQueryParameters params, ReportFormat format) + { + ParameterCheck.mandatory("params", params); + + Writer fileWriter = null; + try + { + File auditTrailFile = TempFileProvider.createTempFile(AUDIT_TRAIL_FILE_PREFIX, + format == ReportFormat.HTML ? AUDIT_TRAIL_HTML_FILE_SUFFIX : AUDIT_TRAIL_JSON_FILE_SUFFIX); + fileWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(auditTrailFile),"UTF8")); + // Get the results, dumping to file + getAuditTrailImpl(params, null, fileWriter, format); + // Done + return auditTrailFile; + } + catch (Throwable e) + { + throw new AlfrescoRuntimeException(MSG_TRAIL_FILE_FAIL, e); + } + finally + { + // close the writer + if (fileWriter != null) + { + try { fileWriter.close(); } catch (IOException closeEx) {} + } + } + } + + /** + * {@inheritDoc} + */ + public List getAuditTrail(RecordsManagementAuditQueryParameters params) + { + ParameterCheck.mandatory("params", params); + + List entries = new ArrayList(50); + try + { + getAuditTrailImpl(params, entries, null, null); + // Done + return entries; + } + catch (Throwable e) + { + // Should be + throw new AlfrescoRuntimeException(MSG_TRAIL_FILE_FAIL, e); + } + } + + /** + * Get the audit trail, optionally dumping the results the the given writer dumping to a list. + * + * @param params the search parameters + * @param results the list to which individual results will be dumped + * @param writer Writer to write the audit trail + * @param reportFormat Format to write the audit trail in, ignored if writer is null + */ + private void getAuditTrailImpl( + RecordsManagementAuditQueryParameters params, + final List results, + final Writer writer, + final ReportFormat reportFormat) + throws IOException + { + if (logger.isDebugEnabled()) + logger.debug("Retrieving audit trail in '" + reportFormat + "' format using parameters: " + params); + + // define the callback + AuditQueryCallback callback = new AuditQueryCallback() + { + private boolean firstEntry = true; + + + public boolean valuesRequired() + { + return true; + } + + /** + * Just log the error, but continue + */ + public boolean handleAuditEntryError(Long entryId, String errorMsg, Throwable error) + { + logger.warn(errorMsg, error); + return true; + } + + @SuppressWarnings("unchecked") + public boolean handleAuditEntry( + Long entryId, + String applicationName, + String user, + long time, + Map values) + { + // Check for context shutdown + if (shutdown) + { + return false; + } + + Date timestamp = new Date(time); + String eventName = null; + String fullName = null; + String userRoles = null; + NodeRef nodeRef = null; + String nodeName = null; + String nodeType = null; + String nodeIdentifier = null; + String namePath = null; + Map beforeProperties = null; + Map afterProperties = null; + + if (values.containsKey(RecordsManagementAuditService.RM_AUDIT_DATA_EVENT_NAME)) + { + // This data is /RM/event/... + eventName = (String) values.get(RecordsManagementAuditService.RM_AUDIT_DATA_EVENT_NAME); + fullName = (String) values.get(RecordsManagementAuditService.RM_AUDIT_DATA_PERSON_FULLNAME); + userRoles = (String) values.get(RecordsManagementAuditService.RM_AUDIT_DATA_PERSON_ROLES); + nodeRef = (NodeRef) values.get(RecordsManagementAuditService.RM_AUDIT_DATA_NODE_NODEREF); + nodeName = (String) values.get(RecordsManagementAuditService.RM_AUDIT_DATA_NODE_NAME); + QName nodeTypeQname = (QName) values.get(RecordsManagementAuditService.RM_AUDIT_DATA_NODE_TYPE); + nodeIdentifier = (String) values.get(RecordsManagementAuditService.RM_AUDIT_DATA_NODE_IDENTIFIER); + namePath = (String) values.get(RecordsManagementAuditService.RM_AUDIT_DATA_NODE_NAMEPATH); + beforeProperties = (Map) values.get( + RecordsManagementAuditService.RM_AUDIT_DATA_NODE_CHANGES_BEFORE); + afterProperties = (Map) values.get( + RecordsManagementAuditService.RM_AUDIT_DATA_NODE_CHANGES_AFTER); + + // Convert some of the values to recognizable forms + nodeType = null; + if (nodeTypeQname != null) + { + TypeDefinition typeDef = dictionaryService.getType(nodeTypeQname); + nodeType = (typeDef != null) ? typeDef.getTitle() : null; + } + } + else if (values.containsKey(RecordsManagementAuditService.RM_AUDIT_DATA_LOGIN_USERNAME)) + { + user = (String) values.get(RecordsManagementAuditService.RM_AUDIT_DATA_LOGIN_USERNAME); + if (values.containsKey(RecordsManagementAuditService.RM_AUDIT_DATA_LOGIN_ERROR)) + { + eventName = RecordsManagementAuditService.RM_AUDIT_EVENT_LOGIN_FAILURE; + fullName = user; // The user didn't log in + } + else + { + eventName = RecordsManagementAuditService.RM_AUDIT_EVENT_LOGIN_SUCCESS; + fullName = (String) values.get(RecordsManagementAuditService.RM_AUDIT_DATA_LOGIN_FULLNAME); + } + } + else + { + // This is not recognisable data + logger.warn( + "Unable to process audit entry for RM. Unexpected data: \n" + + " Entry: " + entryId + "\n" + + " Data: " + values); + // Skip it + return true; + } + + // TODO: Refactor this to use the builder pattern + RecordsManagementAuditEntry entry = new RecordsManagementAuditEntry( + timestamp, + user, + fullName, + userRoles, // A concatenated string of roles + nodeRef, + nodeName, + nodeType, + eventName, + nodeIdentifier, + namePath, + beforeProperties, + afterProperties); + + // write out the entry to the file in requested format + writeEntryToFile(entry); + + if (results != null) + { + results.add(entry); + } + + if (logger.isDebugEnabled()) + { + logger.debug(" " + entry); + } + + // Keep going + return true; + } + + private void writeEntryToFile(RecordsManagementAuditEntry entry) + { + if (writer == null) + { + return; + } + try + { + if (!firstEntry) + { + if (reportFormat == ReportFormat.HTML) + { + writer.write("\n"); + } + else + { + writer.write(","); + } + } + else + { + firstEntry = false; + } + + // write the entry to the file + if (reportFormat == ReportFormat.JSON) + { + writer.write("\n\t\t"); + } + + writeAuditTrailEntry(writer, entry, reportFormat); + } + catch (IOException ioe) + { + throw new AlfrescoRuntimeException(MSG_TRAIL_FILE_FAIL, ioe); + } + } + }; + + String user = params.getUser(); + Long fromTime = (params.getDateFrom() == null ? null : new Long(params.getDateFrom().getTime())); + Long toTime = (params.getDateTo() == null ? null : new Long(params.getDateTo().getTime())); + NodeRef nodeRef = params.getNodeRef(); + String eventName = params.getEvent(); + QName propertyQName = params.getProperty(); + int maxEntries = params.getMaxEntries(); + boolean forward = maxEntries > 0 ? false : true; // Reverse order if the results are limited + + // start the audit trail report + writeAuditTrailHeader(writer, params, reportFormat); + + if (logger.isDebugEnabled()) + { + logger.debug("RM Audit: Issuing query: " + params); + } + + // Build audit query parameters + AuditQueryParameters auditQueryParams = new AuditQueryParameters(); + auditQueryParams.setForward(forward); + auditQueryParams.setApplicationName(RecordsManagementAuditService.RM_AUDIT_APPLICATION_NAME); + auditQueryParams.setUser(user); + auditQueryParams.setFromTime(fromTime); + auditQueryParams.setToTime(toTime); + if (nodeRef != null) + { + auditQueryParams.addSearchKey(RecordsManagementAuditService.RM_AUDIT_DATA_NODE_NODEREF, nodeRef); + } + // Get audit entries + auditService.auditQuery(callback, auditQueryParams, maxEntries); + + // finish off the audit trail report + writeAuditTrailFooter(writer, reportFormat); + } + + /** + * {@inheritDoc} + */ + public NodeRef fileAuditTrailAsRecord(RecordsManagementAuditQueryParameters params, + NodeRef destination, ReportFormat format) + { + ParameterCheck.mandatory("params", params); + ParameterCheck.mandatory("destination", destination); + + // NOTE: the underlying RM services will check all the remaining pre-conditions + + NodeRef record = null; + + // get the audit trail for the provided parameters + File auditTrail = this.getAuditTrailFile(params, format); + + if (logger.isDebugEnabled()) + { + logger.debug("Filing audit trail in file " + auditTrail.getAbsolutePath() + + " as a record in record folder: " + destination); + } + + try + { + Map properties = new HashMap(1); + properties.put(ContentModel.PROP_NAME, auditTrail.getName()); + + // file the audit log as an undeclared record + record = this.nodeService.createNode(destination, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, + QName.createValidLocalName(auditTrail.getName())), + ContentModel.TYPE_CONTENT, properties).getChildRef(); + + // Set the content + ContentWriter writer = this.contentService.getWriter(record, ContentModel.PROP_CONTENT, true); + writer.setMimetype(format == ReportFormat.HTML ? MimetypeMap.MIMETYPE_HTML : MimetypeMap.MIMETYPE_JSON); + writer.setEncoding("UTF-8"); + writer.putContent(auditTrail); + + // file the node as a record + this.rmActionService.executeRecordsManagementAction(record, FILE_ACTION); + } + finally + { + if (logger.isDebugEnabled()) + { + logger.debug("Audit trail report saved to temporary file: " + auditTrail.getAbsolutePath()); + } + else + { + auditTrail.delete(); + } + } + + return record; + } + + /** + * {@inheritDoc} + */ + public List getAuditEvents() + { + List listAuditEvents = new ArrayList(this.auditEvents.size()); + listAuditEvents.addAll(this.auditEvents.values()); + return listAuditEvents; + } + + /** + * Writes the start of the audit trail stream to the given writer + * + * @param writer The writer to write to + * @params params The parameters being used + * @param reportFormat The format to write the header in + * @throws IOException + */ + private void writeAuditTrailHeader(Writer writer, + RecordsManagementAuditQueryParameters params, + ReportFormat reportFormat) throws IOException + { + if (writer == null) + { + return; + } + + if (reportFormat == ReportFormat.HTML) + { + // write header as HTML + writer.write("\n"); + writer.write("\n\n"); + writer.write(""); + writer.write(I18NUtil.getMessage(MSG_AUDIT_REPORT)); + writer.write("\n"); + writer.write("\n"); + writer.write("\n

"); + writer.write(I18NUtil.getMessage(MSG_AUDIT_REPORT)); + writer.write("

\n"); + writer.write("
\n"); + + writer.write("From:"); + writer.write(""); + Date from = params.getDateFrom(); + writer.write(from == null ? "<Not Set>" : StringEscapeUtils.escapeHtml(from.toString())); + writer.write(""); + + writer.write("To:"); + writer.write(""); + Date to = params.getDateTo(); + writer.write(to == null ? "<Not Set>" : StringEscapeUtils.escapeHtml(to.toString())); + writer.write(""); + + writer.write("Property:"); + writer.write(""); + QName prop = params.getProperty(); + writer.write(prop == null ? "All" : StringEscapeUtils.escapeHtml(getPropertyLabel(prop))); + writer.write(""); + + writer.write("User:"); + writer.write(""); + writer.write(params.getUser() == null ? "All" : StringEscapeUtils.escapeHtml(params.getUser())); + writer.write(""); + + writer.write("Event:"); + writer.write(""); + writer.write(params.getEvent() == null ? "All" : StringEscapeUtils.escapeHtml(getAuditEventLabel(params.getEvent()))); + writer.write("\n"); + + writer.write("
\n"); + } + else + { + // write header as JSON + writer.write("{\n\t\"data\":\n\t{"); + writer.write("\n\t\t\"started\": \""); + writer.write(ISO8601DateFormat.format(getDateLastStarted())); + writer.write("\",\n\t\t\"stopped\": \""); + writer.write(ISO8601DateFormat.format(getDateLastStopped())); + writer.write("\",\n\t\t\"enabled\": "); + writer.write(Boolean.toString(isEnabled())); + writer.write(",\n\t\t\"entries\":["); + } + } + + /** + * Writes an audit trail entry to the given writer + * + * @param writer The writer to write to + * @param entry The entry to write + * @param reportFormat The format to write the header in + * @throws IOException + */ + private void writeAuditTrailEntry(Writer writer, RecordsManagementAuditEntry entry, + ReportFormat reportFormat) throws IOException + { + if (writer == null) + { + return; + } + + if (reportFormat == ReportFormat.HTML) + { + writer.write("
\n"); + writer.write("
"); + writer.write("Timestamp:"); + writer.write(""); + writer.write(StringEscapeUtils.escapeHtml(entry.getTimestamp().toString())); + writer.write(""); + writer.write("User:"); + writer.write(""); + writer.write(entry.getFullName() != null ? + StringEscapeUtils.escapeHtml(entry.getFullName()) : + StringEscapeUtils.escapeHtml(entry.getUserName())); + writer.write(""); + if (entry.getUserRole() != null && entry.getUserRole().length() > 0) + { + writer.write("Role:"); + writer.write(""); + writer.write(StringEscapeUtils.escapeHtml(entry.getUserRole())); + writer.write(""); + } + if (entry.getEvent() != null && entry.getEvent().length() > 0) + { + writer.write("Event:"); + writer.write(""); + writer.write(StringEscapeUtils.escapeHtml(getAuditEventLabel(entry.getEvent()))); + writer.write("\n"); + } + writer.write("
\n"); + writer.write("
"); + if (entry.getIdentifier() != null && entry.getIdentifier().length() > 0) + { + writer.write("Identifier:"); + writer.write(""); + writer.write(StringEscapeUtils.escapeHtml(entry.getIdentifier())); + writer.write(""); + } + if (entry.getNodeType() != null && entry.getNodeType().length() > 0) + { + writer.write("Type:"); + writer.write(""); + writer.write(StringEscapeUtils.escapeHtml(entry.getNodeType())); + writer.write(""); + } + if (entry.getPath() != null && entry.getPath().length() > 0) + { + // we need to strip off the first part of the path + String path = entry.getPath(); + String displayPath = path; + int idx = path.indexOf("/", 1); + if (idx != -1) + { + displayPath = "/File Plan" + path.substring(idx); + } + + writer.write("Location:"); + writer.write(""); + writer.write(StringEscapeUtils.escapeHtml(displayPath)); + writer.write(""); + } + writer.write("
\n"); + + if (entry.getChangedProperties() != null) + { + writer.write(""); + writer.write(""); + + // create an entry for each property that changed + for (QName valueName : entry.getChangedProperties().keySet()) + { + Pair values = entry.getChangedProperties().get(valueName); + writer.write(""); + } + + writer.write("
PropertyPrevious ValueNew Value
"); + writer.write(getPropertyLabel(valueName)); + writer.write(""); + Serializable oldValue = values.getFirst(); + writer.write(oldValue == null ? "<none>" : StringEscapeUtils.escapeHtml(oldValue.toString())); + writer.write(""); + Serializable newValue = values.getSecond(); + writer.write(newValue == null ? "<none>" : StringEscapeUtils.escapeHtml(newValue.toString())); + writer.write("
\n"); + } + + writer.write("
"); + } + else + { + try + { + JSONObject json = new JSONObject(); + + json.put("timestamp", entry.getTimestampString()); + json.put("userName", entry.getUserName()); + json.put("userRole", entry.getUserRole() == null ? "": entry.getUserRole()); + json.put("fullName", entry.getFullName() == null ? "": entry.getFullName()); + json.put("nodeRef", entry.getNodeRef() == null ? "": entry.getNodeRef()); + json.put("nodeName", entry.getNodeName() == null ? "": entry.getNodeName()); + json.put("nodeType", entry.getNodeType() == null ? "": entry.getNodeType()); + json.put("event", entry.getEvent() == null ? "": getAuditEventLabel(entry.getEvent())); + json.put("identifier", entry.getIdentifier() == null ? "": entry.getIdentifier()); + json.put("path", entry.getPath() == null ? "": entry.getPath()); + + JSONArray changedValues = new JSONArray(); + + if (entry.getChangedProperties() != null) + { + // create an entry for each property that changed + for (QName valueName : entry.getChangedProperties().keySet()) + { + Pair values = entry.getChangedProperties().get(valueName); + + JSONObject changedValue = new JSONObject(); + changedValue.put("name", getPropertyLabel(valueName)); + changedValue.put("previous", values.getFirst() == null ? "" : values.getFirst().toString()); + changedValue.put("new", values.getSecond() == null ? "" : values.getSecond().toString()); + + changedValues.put(changedValue); + } + } + + json.put("changedValues", changedValues); + + writer.write(json.toString()); + } + catch (JSONException je) + { + writer.write("{}"); + } + } + } + + /** + * Writes the end of the audit trail stream to the given writer + * + * @param writer The writer to write to + * @param reportFormat The format to write the footer in + * @throws IOException + */ + private void writeAuditTrailFooter(Writer writer, ReportFormat reportFormat) throws IOException + { + if (writer == null) + { + return; + } + + if (reportFormat == ReportFormat.HTML) + { + // write footer as HTML + writer.write("\n"); + } + else + { + // write footer as JSON + writer.write("\n\t\t]\n\t}\n}"); + } + } + + /** + * Returns the display label for a property QName + * + * @param property The property to get label for + * @param ddService DictionaryService instance + * @param namespaceService NamespaceService instance + * @return The label + */ + private String getPropertyLabel(QName property) + { + String label = null; + + PropertyDefinition propDef = this.dictionaryService.getProperty(property); + if (propDef != null) + { + label = propDef.getTitle(); + } + + if (label == null) + { + label = property.getLocalName(); + } + + return label; + } + + /** + * Returns the display label for the given audit event key + * + * @param eventKey The audit event key + * @return The display label or null if the key does not exist + */ + private String getAuditEventLabel(String eventKey) + { + String label = eventKey; + + AuditEvent event = this.auditEvents.get(eventKey); + if (event != null) + { + label = event.getLabel(); + } + + return label; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/AbstractCapability.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/AbstractCapability.java new file mode 100644 index 0000000000..9e8f60e7e3 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/AbstractCapability.java @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability; + +import java.util.ArrayList; +import java.util.List; + +import net.sf.acegisecurity.vote.AccessDecisionVoter; + +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.AccessStatus; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Abstract capability implementation. + * + * @author Andy Hind + * @author Roy Wetherall + */ +public abstract class AbstractCapability extends RMSecurityCommon + implements Capability, RecordsManagementModel, RMPermissionModel +{ + /** Logger */ + @SuppressWarnings("unused") + private static Log logger = LogFactory.getLog(AbstractCapability.class); + + /** RM entry voter */ + protected RMEntryVoter voter; + + /** Capability service */ + protected CapabilityService capabilityService; + + /** Capability name */ + protected String name; + + /** Indicates whether this is a private capability or not */ + protected boolean isPrivate = false; + + /** List of actions */ + protected List actions = new ArrayList(1); + + /** Action names */ + protected List actionNames = new ArrayList(1); + + /** + * @param voter RM entry voter + */ + public void setVoter(RMEntryVoter voter) + { + this.voter = voter; + } + + /** + * @param capabilityService capability service + */ + public void setCapabilityService(CapabilityService capabilityService) + { + this.capabilityService = capabilityService; + } + + /** + * Init method + */ + public void init() + { + capabilityService.registerCapability(this); + } + + /** + * Registers an action + * + * @param action + */ + public void registerAction(RecordsManagementAction action) + { + this.actions.add(action); + this.actionNames.add(action.getName()); + voter.addProtectedAspects(action.getProtectedAspects()); + voter.addProtectedProperties(action.getProtectedProperties()); + } + + /** + * @param name capability name + */ + public void setName(String name) + { + this.name = name; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.capability.Capability#getName() + */ + @Override + public String getName() + { + return name; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.capability.Capability#isPrivate() + */ + public boolean isPrivate() + { + return isPrivate; + } + + /** + * @param isPrivate indicates whether the capability is private or not + */ + public void setPrivate(boolean isPrivate) + { + this.isPrivate = isPrivate; + } + + /** + * Translates the vote to an AccessStatus + * + * @param vote + * @return + */ + private AccessStatus translate(int vote) + { + switch (vote) + { + case AccessDecisionVoter.ACCESS_ABSTAIN: + return AccessStatus.UNDETERMINED; + case AccessDecisionVoter.ACCESS_GRANTED: + return AccessStatus.ALLOWED; + case AccessDecisionVoter.ACCESS_DENIED: + return AccessStatus.DENIED; + default: + return AccessStatus.UNDETERMINED; + } + } + + /** + * + * @param nodeRef + * @return + */ + public int checkActionConditionsIfPresent(NodeRef nodeRef) + { + String prefix = "checkActionConditionsIfPresent" + getName(); + int result = getTransactionCache(prefix, nodeRef); + if (result != NOSET_VALUE) + { + return result; + } + + if (actions.size() > 0) + { + for (RecordsManagementAction action : actions) + { + if (action.isExecutable(nodeRef, null)) + { + return setTransactionCache(prefix, nodeRef, AccessDecisionVoter.ACCESS_GRANTED); + } + } + return setTransactionCache(prefix, nodeRef, AccessDecisionVoter.ACCESS_DENIED); + } + else + { + return setTransactionCache(prefix, nodeRef, AccessDecisionVoter.ACCESS_GRANTED); + } + } + + public AccessStatus hasPermission(NodeRef nodeRef) + { + return translate(hasPermissionRaw(nodeRef)); + } + + public int hasPermissionRaw(NodeRef nodeRef) + { + String prefix = "hasPermissionRaw" + getName(); + int result = getTransactionCache(prefix, nodeRef); + if (result != NOSET_VALUE) + { + return result; + } + + if (checkRmRead(nodeRef) == AccessDecisionVoter.ACCESS_DENIED) + { + result = AccessDecisionVoter.ACCESS_DENIED; + } + else if (checkActionConditionsIfPresent(nodeRef) == AccessDecisionVoter.ACCESS_DENIED) + { + result = AccessDecisionVoter.ACCESS_DENIED; + } + else + { + result = hasPermissionImpl(nodeRef); + } + + return setTransactionCache(prefix, nodeRef, result); + } + + /** + * Default implementation. Override if different behaviour required. + * + * @param nodeRef + * @return + */ + protected int hasPermissionImpl(NodeRef nodeRef) + { + return evaluate(nodeRef); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.capability.Capability#evaluate(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef) + */ + public int evaluate(NodeRef source, NodeRef target) + { + return AccessDecisionVoter.ACCESS_ABSTAIN; + } + + public List getActionNames() + { + return actionNames; + } + + public List getActions() + { + return actions; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((getName() == null) ? 0 : getName().hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + final AbstractCapability other = (AbstractCapability) obj; + if (getName() == null) + { + if (other.getName() != null) + return false; + } + else if (!getName().equals(other.getName())) + return false; + return true; + } + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/Capability.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/Capability.java new file mode 100644 index 0000000000..7b0eb3c35d --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/Capability.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability; + +import java.util.List; + +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.AccessStatus; + +/** + * Capability Interface. + * + * @author andyh + */ +public interface Capability +{ + /** + * Does this capability apply to this nodeRef? + * @param nodeRef + * @return + */ + AccessStatus hasPermission(NodeRef nodeRef); + + /** + * + * @param nodeRef + * @return + */ + int hasPermissionRaw(NodeRef nodeRef); + + /** + * Evaluates the capability. + * + * @param nodeRef + * @return + */ + int evaluate(NodeRef nodeRef); + + /** + * + * @param source + * @param target + * @return + */ + int evaluate(NodeRef source, NodeRef target); + + /** + * Indicates whether this is a private capability or not. Private capabilities are used internally, otherwise + * they are made available to the user to assign to roles. + * + * @return + */ + boolean isPrivate(); + + /** + * Get the name of the capability + * @return + */ + String getName(); + + /** + * Get the name of optional actions tied to this capability + * @return + */ + List getActionNames(); + + /** + * + * @return + */ + List getActions(); +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/CapabilityService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/CapabilityService.java new file mode 100644 index 0000000000..71b50ef918 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/CapabilityService.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.AccessStatus; + +/** + * Capability service implementation + * + * @author Roy Wetherall + * @since 2.0 + */ +public interface CapabilityService +{ + /** + * Register a capability + * + * @param capability capability + */ + void registerCapability(Capability capability); + + /** + * Get a named capability. + * + * @param name capability name + * @return {@link Capability} capability or null if not found + */ + Capability getCapability(String name); + + /** + * + * @return + */ + Set getCapabilities(); + + /** + * + * @param nodeRef + * @return + */ + Map getCapabilitiesAccessState(NodeRef nodeRef); + + /** + * + * @param nodeRef + * @param capabilityNames + * @return + */ + Map getCapabilitiesAccessState(NodeRef nodeRef, List capabilityNames); +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/CapabilityServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/CapabilityServiceImpl.java new file mode 100644 index 0000000000..4eabad974f --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/CapabilityServiceImpl.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.AccessStatus; + +/** + * @author Roy Wetherall + * @since 2.0 + */ +public class CapabilityServiceImpl implements CapabilityService +{ + /** Capabilities */ + private Map capabilities = new HashMap(57); + + /** + * @see org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService#getCapability(java.lang.String) + */ + @Override + public Capability getCapability(String name) + { + return capabilities.get(name); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService#registerCapability(org.alfresco.module.org_alfresco_module_rm.capability.Capability) + */ + @Override + public void registerCapability(Capability capability) + { + capabilities.put(capability.getName(), capability); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService#getCapabilities() + */ + @Override + public Set getCapabilities() + { + return new HashSet(capabilities.values()); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService#getCapabilitiesAccessState(org.alfresco.service.cmr.repository.NodeRef) + */ + public Map getCapabilitiesAccessState(NodeRef nodeRef) + { + HashMap answer = new HashMap(); + for (Capability capability : capabilities.values()) + { + AccessStatus status = capability.hasPermission(nodeRef); + if (answer.put(capability, status) != null) + { + throw new IllegalStateException(); + } + } + return answer; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService#getCapabilitiesAccessState(org.alfresco.service.cmr.repository.NodeRef, java.util.List) + */ + public Map getCapabilitiesAccessState(NodeRef nodeRef, List capabilityNames) + { + HashMap answer = new HashMap(); + for (String capabilityName : capabilityNames) + { + Capability capability = capabilities.get(capabilityName); + if (capability != null) + { + AccessStatus status = capability.hasPermission(nodeRef); + if (answer.put(capability, status) != null) + { + throw new IllegalStateException(); + } + } + } + return answer; + } + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMActionProxyFactoryBean.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMActionProxyFactoryBean.java new file mode 100644 index 0000000000..5470a46f85 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMActionProxyFactoryBean.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability; + +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; +import org.alfresco.repo.action.RuntimeActionService; +import org.alfresco.repo.action.executer.ActionExecuter; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.springframework.aop.framework.ProxyFactoryBean; + +public class RMActionProxyFactoryBean extends ProxyFactoryBean +{ + private static final long serialVersionUID = 539749542853266449L; + + protected RuntimeActionService runtimeActionService; + + private RecordsManagementActionService recordsManagementActionService; + + /** + * Set action service + * + * @param actionService + */ + public void setRuntimeActionService(RuntimeActionService runtimeActionService) + { + this.runtimeActionService = runtimeActionService; + } + + /** + * Set records management service + * + * @param recordsManagementActionService + */ + public void setRecordsManagementActionService(RecordsManagementActionService recordsManagementActionService) + { + this.recordsManagementActionService = recordsManagementActionService; + } + + public void registerAction() + { + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Void doWork() throws Exception + { + runtimeActionService.registerActionExecuter((ActionExecuter) getObject()); + recordsManagementActionService.register((RecordsManagementAction) getObject()); + return null; + } + }, AuthenticationUtil.getSystemUserName()); + + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMAfterInvocationProvider.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMAfterInvocationProvider.java new file mode 100644 index 0000000000..672ec6e8a7 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMAfterInvocationProvider.java @@ -0,0 +1,977 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability; + +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; + +import net.sf.acegisecurity.AccessDeniedException; +import net.sf.acegisecurity.Authentication; +import net.sf.acegisecurity.ConfigAttribute; +import net.sf.acegisecurity.ConfigAttributeDefinition; +import net.sf.acegisecurity.afterinvocation.AfterInvocationProvider; +import net.sf.acegisecurity.vote.AccessDecisionVoter; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.search.SimpleResultSetMetaData; +import org.alfresco.repo.search.impl.lucene.PagingLuceneResultSet; +import org.alfresco.repo.search.impl.querymodel.QueryEngineResults; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.permissions.PermissionCheckCollection; +import org.alfresco.repo.security.permissions.PermissionCheckValue; +import org.alfresco.repo.security.permissions.PermissionCheckedValue; +import org.alfresco.repo.security.permissions.PermissionCheckedCollection.PermissionCheckedCollectionMixin; +import org.alfresco.repo.security.permissions.impl.acegi.ACLEntryVoterException; +import org.alfresco.repo.security.permissions.impl.acegi.FilteringResultSet; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.search.LimitBy; +import org.alfresco.service.cmr.search.PermissionEvaluationMode; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.security.AccessStatus; +import org.aopalliance.intercept.MethodInvocation; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.InitializingBean; + +public class RMAfterInvocationProvider extends RMSecurityCommon + implements AfterInvocationProvider, InitializingBean +{ + private static Log logger = LogFactory.getLog(RMAfterInvocationProvider.class); + + private static final String AFTER_RM = "AFTER_RM"; + + private int maxPermissionChecks; + + private long maxPermissionCheckTimeMillis; + + public boolean supports(ConfigAttribute attribute) + { + if ((attribute.getAttribute() != null) && (attribute.getAttribute().startsWith(AFTER_RM))) + { + return true; + } + else + { + return false; + } + } + + @SuppressWarnings("unchecked") + public boolean supports(Class clazz) + { + return (MethodInvocation.class.isAssignableFrom(clazz)); + } + + public void afterPropertiesSet() throws Exception + { + } + + /** + * Default constructor + */ + public RMAfterInvocationProvider() + { + super(); + maxPermissionChecks = Integer.MAX_VALUE; + maxPermissionCheckTimeMillis = Long.MAX_VALUE; + } + + /** + * Set the max number of permission checks + * + * @param maxPermissionChecks + */ + public void setMaxPermissionChecks(int maxPermissionChecks) + { + this.maxPermissionChecks = maxPermissionChecks; + } + + /** + * Set the max time for permission checks + * + * @param maxPermissionCheckTimeMillis + */ + public void setMaxPermissionCheckTimeMillis(long maxPermissionCheckTimeMillis) + { + this.maxPermissionCheckTimeMillis = maxPermissionCheckTimeMillis; + } + + @SuppressWarnings("unchecked") + public Object decide(Authentication authentication, Object object, ConfigAttributeDefinition config, Object returnedObject) throws AccessDeniedException + { + if (logger.isDebugEnabled()) + { + MethodInvocation mi = (MethodInvocation) object; + if (mi == null) + { + logger.debug("Method is null."); + } + else + { + logger.debug("Method: " + mi.getMethod().toString()); + } + } + try + { + if (AuthenticationUtil.isRunAsUserTheSystemUser()) + { + if (logger.isDebugEnabled()) + { + logger.debug("Allowing system user access"); + } + return returnedObject; + } + else if (returnedObject == null) + { + if (logger.isDebugEnabled()) + { + logger.debug("Allowing null object access"); + } + return null; + } + else if (PermissionCheckedValue.class.isAssignableFrom(returnedObject.getClass())) + { + return decide(authentication, object, config, (PermissionCheckedValue) returnedObject); + } + else if (PermissionCheckValue.class.isAssignableFrom(returnedObject.getClass())) + { + return decide(authentication, object, config, (PermissionCheckValue) returnedObject); + } + else if (StoreRef.class.isAssignableFrom(returnedObject.getClass())) + { + return decide(authentication, object, config, nodeService.getRootNode((StoreRef) returnedObject)).getStoreRef(); + } + else if (NodeRef.class.isAssignableFrom(returnedObject.getClass())) + { + return decide(authentication, object, config, (NodeRef) returnedObject); + } + else if (ChildAssociationRef.class.isAssignableFrom(returnedObject.getClass())) + { + return decide(authentication, object, config, (ChildAssociationRef) returnedObject); + } + else if (AssociationRef.class.isAssignableFrom(returnedObject.getClass())) + { + return decide(authentication, object, config, (AssociationRef) returnedObject); + } + else if (ResultSet.class.isAssignableFrom(returnedObject.getClass())) + { + return decide(authentication, object, config, (ResultSet) returnedObject); + } + else if (PagingLuceneResultSet.class.isAssignableFrom(returnedObject.getClass())) + { + return decide(authentication, object, config, (PagingLuceneResultSet) returnedObject); + } + else if (QueryEngineResults.class.isAssignableFrom(returnedObject.getClass())) + { + return decide(authentication, object, config, (QueryEngineResults) returnedObject); + } + else if (Collection.class.isAssignableFrom(returnedObject.getClass())) + { + return decide(authentication, object, config, (Collection) returnedObject); + } + else if (returnedObject.getClass().isArray()) + { + return decide(authentication, object, config, (Object[]) returnedObject); + } + else if (Map.class.isAssignableFrom(returnedObject.getClass())) + { + return decide(authentication, object, config, (Map) returnedObject); + } + else + { + if (logger.isDebugEnabled()) + { + logger.debug("Uncontrolled object - access allowed for " + object.getClass().getName()); + } + return returnedObject; + } + } + catch (AccessDeniedException ade) + { + if (logger.isDebugEnabled()) + { + logger.debug("Access denied"); + ade.printStackTrace(); + } + throw ade; + } + catch (RuntimeException re) + { + if (logger.isDebugEnabled()) + { + logger.debug("Access denied by runtime exception"); + re.printStackTrace(); + } + throw re; + } + + } + + private PermissionCheckedValue decide(Authentication authentication, Object object, ConfigAttributeDefinition config, PermissionCheckedValue returnedObject) throws AccessDeniedException + { + // This passes as it has already been filtered + // TODO: Get the filter that was applied and double-check + return returnedObject; + } + + private PermissionCheckValue decide(Authentication authentication, Object object, ConfigAttributeDefinition config, PermissionCheckValue returnedObject) throws AccessDeniedException + { + // Get the wrapped value + NodeRef nodeRef = returnedObject.getNodeRef(); + decide(authentication, object, config, nodeRef); + // This passes + return returnedObject; + } + + private NodeRef decide(Authentication authentication, Object object, ConfigAttributeDefinition config, NodeRef returnedObject) throws AccessDeniedException + + { + if (returnedObject == null) + { + return null; + } + + if (isUnfiltered(returnedObject)) + { + return returnedObject; + } + + List supportedDefinitions = extractSupportedDefinitions(config); + if (supportedDefinitions.size() == 0) + { + return returnedObject; + } + + int parentResult = checkRead(nodeService.getPrimaryParent(returnedObject).getParentRef()); + int childResult = checkRead(returnedObject); + checkSupportedDefinitions(supportedDefinitions, parentResult, childResult); + + return returnedObject; + } + + private void checkSupportedDefinitions(List supportedDefinitions, int parentResult, int childResult) + { + for (ConfigAttributeDefintion cad : supportedDefinitions) + { + if (cad.parent == true && parentResult == AccessDecisionVoter.ACCESS_DENIED) + { + throw new AccessDeniedException("Access Denied"); + } + else if (cad.parent == false && childResult == AccessDecisionVoter.ACCESS_DENIED) + { + throw new AccessDeniedException("Access Denied"); + } + } + } + + private boolean isUnfiltered(NodeRef nodeRef) + { + return !nodeService.hasAspect(nodeRef, RecordsManagementModel.ASPECT_FILE_PLAN_COMPONENT); + + } + + @SuppressWarnings({"unchecked" }) + private List extractSupportedDefinitions(ConfigAttributeDefinition config) + { + List definitions = new ArrayList(); + Iterator iter = config.getConfigAttributes(); + + while (iter.hasNext()) + { + ConfigAttribute attr = (ConfigAttribute) iter.next(); + + if (this.supports(attr)) + { + definitions.add(new ConfigAttributeDefintion(attr)); + } + + } + return definitions; + } + + private ChildAssociationRef decide(Authentication authentication, Object object, ConfigAttributeDefinition config, ChildAssociationRef returnedObject) + throws AccessDeniedException + + { + if (returnedObject == null) + { + return null; + } + + List supportedDefinitions = extractSupportedDefinitions(config); + + if (supportedDefinitions.size() == 0) + { + return returnedObject; + } + + int parentReadCheck = checkRead(returnedObject.getParentRef()); + int childReadCheck = checkRead(returnedObject.getChildRef()); + + for (ConfigAttributeDefintion cad : supportedDefinitions) + { + NodeRef testNodeRef = null; + + if (cad.typeString.equals(cad.parent) == true) + { + testNodeRef = returnedObject.getParentRef(); + } + else + { + testNodeRef = returnedObject.getChildRef(); + } + + // Enforce Read Policy + + if (isUnfiltered(testNodeRef)) + { + continue; + } + + if (cad.typeString.equals(cad.parent) == true && parentReadCheck != AccessDecisionVoter.ACCESS_GRANTED) + { + throw new AccessDeniedException("Access Denied"); + } + else if (childReadCheck != AccessDecisionVoter.ACCESS_GRANTED) + { + throw new AccessDeniedException("Access Denied"); + } + } + + return returnedObject; + } + + private AssociationRef decide(Authentication authentication, Object object, ConfigAttributeDefinition config, AssociationRef returnedObject) throws AccessDeniedException + + { + if (returnedObject == null) + { + return null; + } + + List supportedDefinitions = extractSupportedDefinitions(config); + + if (supportedDefinitions.size() == 0) + { + return returnedObject; + } + + for (ConfigAttributeDefintion cad : supportedDefinitions) + { + NodeRef testNodeRef = null; + + if (cad.parent) + { + testNodeRef = returnedObject.getSourceRef(); + } + else + { + testNodeRef = returnedObject.getTargetRef(); + } + + if (isUnfiltered(testNodeRef)) + { + continue; + } + + if (checkRead(testNodeRef) != AccessDecisionVoter.ACCESS_GRANTED) + { + throw new AccessDeniedException("Access Denied"); + } + + } + + return returnedObject; + } + + private ResultSet decide(Authentication authentication, Object object, ConfigAttributeDefinition config, PagingLuceneResultSet returnedObject) throws AccessDeniedException + + { + ResultSet raw = returnedObject.getWrapped(); + ResultSet filteredForPermissions = decide(authentication, object, config, raw); + PagingLuceneResultSet newPaging = new PagingLuceneResultSet(filteredForPermissions, returnedObject.getResultSetMetaData().getSearchParameters(), nodeService); + return newPaging; + } + + private ResultSet decide(Authentication authentication, Object object, ConfigAttributeDefinition config, ResultSet returnedObject) throws AccessDeniedException + + { + if (returnedObject == null) + { + return null; + } + + BitSet inclusionMask = new BitSet(returnedObject.length()); + FilteringResultSet filteringResultSet = new FilteringResultSet(returnedObject, inclusionMask); + + List supportedDefinitions = extractSupportedDefinitions(config); + + Integer maxSize = null; + if (returnedObject.getResultSetMetaData().getSearchParameters().getMaxItems() >= 0) + { + maxSize = new Integer(returnedObject.getResultSetMetaData().getSearchParameters().getMaxItems()); + } + if ((maxSize == null) && (returnedObject.getResultSetMetaData().getSearchParameters().getLimitBy() == LimitBy.FINAL_SIZE)) + { + maxSize = new Integer(returnedObject.getResultSetMetaData().getSearchParameters().getLimit()); + } + // Allow for skip + if ((maxSize != null) && (returnedObject.getResultSetMetaData().getSearchParameters().getSkipCount() >= 0)) + { + maxSize = new Integer(maxSize + returnedObject.getResultSetMetaData().getSearchParameters().getSkipCount()); + } + + int maxChecks = maxPermissionChecks; + if (returnedObject.getResultSetMetaData().getSearchParameters().getMaxPermissionChecks() >= 0) + { + maxChecks = returnedObject.getResultSetMetaData().getSearchParameters().getMaxPermissionChecks(); + } + + long maxCheckTime = maxPermissionCheckTimeMillis; + if (returnedObject.getResultSetMetaData().getSearchParameters().getMaxPermissionCheckTimeMillis() >= 0) + { + maxCheckTime = returnedObject.getResultSetMetaData().getSearchParameters().getMaxPermissionCheckTimeMillis(); + } + + if (supportedDefinitions.size() == 0) + { + if (maxSize == null) + { + return returnedObject; + } + else if (returnedObject.length() > maxSize.intValue()) + { + for (int i = 0; i < maxSize.intValue(); i++) + { + inclusionMask.set(i, true); + } + filteringResultSet.setResultSetMetaData(new SimpleResultSetMetaData(returnedObject.getResultSetMetaData().getLimitedBy(), PermissionEvaluationMode.EAGER, returnedObject.getResultSetMetaData() + .getSearchParameters())); + return filteringResultSet; + } + else + { + for (int i = 0; i < returnedObject.length(); i++) + { + inclusionMask.set(i, true); + } + filteringResultSet.setResultSetMetaData(new SimpleResultSetMetaData(returnedObject.getResultSetMetaData().getLimitedBy(), PermissionEvaluationMode.EAGER, returnedObject.getResultSetMetaData() + .getSearchParameters())); + return filteringResultSet; + } + } + + // record the start time + long startTimeMillis = System.currentTimeMillis(); + // set the default, unlimited resultset type + filteringResultSet.setResultSetMetaData(new SimpleResultSetMetaData(returnedObject.getResultSetMetaData().getLimitedBy(), PermissionEvaluationMode.EAGER, returnedObject.getResultSetMetaData() + .getSearchParameters())); + + for (int i = 0; i < returnedObject.length(); i++) + { + long currentTimeMillis = System.currentTimeMillis(); + if (i >= maxChecks || (currentTimeMillis - startTimeMillis) > maxCheckTime) + { + filteringResultSet.setResultSetMetaData(new SimpleResultSetMetaData(LimitBy.NUMBER_OF_PERMISSION_EVALUATIONS, PermissionEvaluationMode.EAGER, returnedObject + .getResultSetMetaData().getSearchParameters())); + break; + } + + // All permission checks must pass + inclusionMask.set(i, true); + + int parentCheckRead = checkRead(returnedObject.getChildAssocRef(i).getParentRef()); + int childCheckRead = checkRead(returnedObject.getNodeRef(i)); + + for (ConfigAttributeDefintion cad : supportedDefinitions) + { + NodeRef testNodeRef = returnedObject.getNodeRef(i); + int checkRead = childCheckRead; + if (cad.parent) + { + testNodeRef = returnedObject.getChildAssocRef(i).getParentRef(); + checkRead = parentCheckRead; + } + + if (isUnfiltered(testNodeRef)) + { + continue; + } + + if (inclusionMask.get(i) && (testNodeRef != null) && (checkRead != AccessDecisionVoter.ACCESS_GRANTED)) + { + inclusionMask.set(i, false); + } + } + + // Bug out if we are limiting by size + if ((maxSize != null) && (filteringResultSet.length() > maxSize.intValue())) + { + // Remove the last match to fix the correct size + inclusionMask.set(i, false); + filteringResultSet.setResultSetMetaData(new SimpleResultSetMetaData(LimitBy.FINAL_SIZE, PermissionEvaluationMode.EAGER, returnedObject.getResultSetMetaData() + .getSearchParameters())); + break; + } + } + return filteringResultSet; + } + + private QueryEngineResults decide(Authentication authentication, Object object, ConfigAttributeDefinition config, QueryEngineResults returnedObject) + throws AccessDeniedException + + { + Map, ResultSet> map = returnedObject.getResults(); + Map, ResultSet> answer = new HashMap, ResultSet>(map.size(), 1.0f); + + for (Set group : map.keySet()) + { + ResultSet raw = map.get(group); + ResultSet permed; + if (PagingLuceneResultSet.class.isAssignableFrom(raw.getClass())) + { + permed = decide(authentication, object, config, (PagingLuceneResultSet) raw); + } + else + { + permed = decide(authentication, object, config, raw); + } + answer.put(group, permed); + } + return new QueryEngineResults(answer); + } + + @SuppressWarnings({ "unchecked" }) + private Collection decide(Authentication authentication, Object object, ConfigAttributeDefinition config, Collection returnedObject) throws AccessDeniedException + { + if (returnedObject == null) + { + return null; + } + + List supportedDefinitions = extractSupportedDefinitions(config); + if (logger.isDebugEnabled()) + { + logger.debug("Entries are " + supportedDefinitions); + } + + if (supportedDefinitions.size() == 0) + { + return returnedObject; + } + + // Default to the system-wide values and we'll see if they need to be reduced + long targetResultCount = returnedObject.size(); + int maxPermissionChecks = Integer.MAX_VALUE; + long maxPermissionCheckTimeMillis = this.maxPermissionCheckTimeMillis; + if (returnedObject instanceof PermissionCheckCollection) + { + PermissionCheckCollection permissionCheckCollection = (PermissionCheckCollection) returnedObject; + // Get values + targetResultCount = permissionCheckCollection.getTargetResultCount(); + if (permissionCheckCollection.getCutOffAfterCount() > 0) + { + maxPermissionChecks = permissionCheckCollection.getCutOffAfterCount(); + } + if (permissionCheckCollection.getCutOffAfterTimeMs() > 0) + { + maxPermissionCheckTimeMillis = permissionCheckCollection.getCutOffAfterTimeMs(); + } + } + + // Start timer and counter for cut-off + boolean cutoff = false; + long startTimeMillis = System.currentTimeMillis(); + int count = 0; + + // Keep values explicitly + List keepValues = new ArrayList(returnedObject.size()); + + for (Object nextObject : returnedObject) + { + // if the maximum result size or time has been exceeded, then we have to remove only + long currentTimeMillis = System.currentTimeMillis(); + + // NOTE: for reference - the "maxPermissionChecks" has never been honoured by this loop (since previously the count was not being incremented) + if (count >= targetResultCount) + { + // We have enough results. We stop without cutoff. + break; + } + else if (count >= maxPermissionChecks) + { + // We have been cut off by count + cutoff = true; + if (logger.isDebugEnabled()) + { + logger.debug("decide (collection) cut-off: " + count + " checks exceeded " + maxPermissionChecks + " checks"); + } + break; + } + else if ((currentTimeMillis - startTimeMillis) > maxPermissionCheckTimeMillis) + { + // We have been cut off by time + cutoff = true; + if (logger.isDebugEnabled()) + { + logger.debug("decide (collection) cut-off: " + (currentTimeMillis - startTimeMillis) + "ms exceeded " + maxPermissionCheckTimeMillis + "ms"); + } + break; + } + + boolean allowed = true; + for (ConfigAttributeDefintion cad : supportedDefinitions) + { + if (cad.mode.equalsIgnoreCase("FilterNode")) + { + NodeRef testNodeRef = null; + if (cad.parent) + { + if (StoreRef.class.isAssignableFrom(nextObject.getClass())) + { + // Will be allowed + testNodeRef = null; + } + else if (NodeRef.class.isAssignableFrom(nextObject.getClass())) + { + testNodeRef = nodeService.getPrimaryParent((NodeRef) nextObject).getParentRef(); + } + else if (ChildAssociationRef.class.isAssignableFrom(nextObject.getClass())) + { + testNodeRef = ((ChildAssociationRef) nextObject).getParentRef(); + } + else if (AssociationRef.class.isAssignableFrom(nextObject.getClass())) + { + testNodeRef = ((AssociationRef) nextObject).getSourceRef(); + } + else if (PermissionCheckValue.class.isAssignableFrom(nextObject.getClass())) + { + NodeRef nodeRef = ((PermissionCheckValue) nextObject).getNodeRef(); + testNodeRef = nodeService.getPrimaryParent(nodeRef).getParentRef(); + } + else + { + throw new ACLEntryVoterException("The specified parameter is recognized: " + nextObject.getClass()); + } + } + else + { + if (StoreRef.class.isAssignableFrom(nextObject.getClass())) + { + testNodeRef = nodeService.getRootNode((StoreRef) nextObject); + } + else if (NodeRef.class.isAssignableFrom(nextObject.getClass())) + { + testNodeRef = (NodeRef) nextObject; + } + else if (ChildAssociationRef.class.isAssignableFrom(nextObject.getClass())) + { + testNodeRef = ((ChildAssociationRef) nextObject).getChildRef(); + } + else if (AssociationRef.class.isAssignableFrom(nextObject.getClass())) + { + testNodeRef = ((AssociationRef) nextObject).getTargetRef(); + } + else if (PermissionCheckValue.class.isAssignableFrom(nextObject.getClass())) + { + testNodeRef = ((PermissionCheckValue) nextObject).getNodeRef(); + } + else + { + throw new ACLEntryVoterException("The specified parameter is recognized: " + nextObject.getClass()); + } + } + + if (logger.isDebugEnabled()) + { + logger.debug("\t" + cad.typeString + " test on " + testNodeRef + " from " + nextObject.getClass().getName()); + } + + if (isUnfiltered(testNodeRef)) // Null allows + { + continue; // Continue to next ConfigAttributeDefintion + } + + if (allowed && (testNodeRef != null) && (checkRead(testNodeRef) != AccessDecisionVoter.ACCESS_GRANTED)) + { + allowed = false; + break; // No point evaluating more ConfigAttributeDefintions + } + } + } + + // Failure or success, increase the count + count++; + + if (allowed) + { + keepValues.add(nextObject); + } + } + // Work out how many were left unchecked (for whatever reason) + int sizeOriginal = returnedObject.size(); + int checksRemaining = sizeOriginal - count; + // Note: There are use-cases where unmodifiable collections are passing through. + // So make sure that the collection needs modification at all + if (keepValues.size() < sizeOriginal) + { + // There are values that need to be removed. We have to modify the collection. + try + { + returnedObject.clear(); + returnedObject.addAll(keepValues); + } + catch (UnsupportedOperationException e) + { + throw new AccessDeniedException("Permission-checked list must be modifiable", e); + } + } + + // Attach the extra permission-check data to the collection + return PermissionCheckedCollectionMixin.create(returnedObject, cutoff, checksRemaining, sizeOriginal); + } + + private Object[] decide(Authentication authentication, Object object, ConfigAttributeDefinition config, Object[] returnedObject) throws AccessDeniedException + { + // Assumption: value is not null + BitSet incudedSet = new BitSet(returnedObject.length); + + List supportedDefinitions = extractSupportedDefinitions(config); + + if (supportedDefinitions.size() == 0) + { + return returnedObject; + } + + for (int i = 0, l = returnedObject.length; i < l; i++) + { + Object current = returnedObject[i]; + + int parentReadCheck = checkRead(getParentReadCheckNode(current)); + int childReadChek = checkRead(getChildReadCheckNode(current)); + + for (ConfigAttributeDefintion cad : supportedDefinitions) + { + incudedSet.set(i, true); + NodeRef testNodeRef = null; + if (cad.parent) + { + if (StoreRef.class.isAssignableFrom(current.getClass())) + { + testNodeRef = null; + } + else if (NodeRef.class.isAssignableFrom(current.getClass())) + { + testNodeRef = nodeService.getPrimaryParent((NodeRef) current).getParentRef(); + } + else if (ChildAssociationRef.class.isAssignableFrom(current.getClass())) + { + testNodeRef = ((ChildAssociationRef) current).getParentRef(); + } + else if (PermissionCheckValue.class.isAssignableFrom(current.getClass())) + { + NodeRef nodeRef = ((PermissionCheckValue) current).getNodeRef(); + testNodeRef = nodeService.getPrimaryParent(nodeRef).getParentRef(); + } + else + { + throw new ACLEntryVoterException("The specified parameter is recognized: " + current.getClass()); + } + } + else + { + if (StoreRef.class.isAssignableFrom(current.getClass())) + { + testNodeRef = nodeService.getRootNode((StoreRef) current); + } + else if (NodeRef.class.isAssignableFrom(current.getClass())) + { + testNodeRef = (NodeRef) current; + } + else if (ChildAssociationRef.class.isAssignableFrom(current.getClass())) + { + testNodeRef = ((ChildAssociationRef) current).getChildRef(); + } + else if (PermissionCheckValue.class.isAssignableFrom(current.getClass())) + { + testNodeRef = ((PermissionCheckValue) current).getNodeRef(); + } + else + { + throw new ACLEntryVoterException("The specified parameter is recognized: " + current.getClass()); + } + } + + if (logger.isDebugEnabled()) + { + logger.debug("\t" + cad.typeString + " test on " + testNodeRef + " from " + current.getClass().getName()); + } + + if (isUnfiltered(testNodeRef)) + { + continue; + } + + int readCheck = childReadChek; + if (cad.parent == true) + { + readCheck = parentReadCheck; + } + + if (incudedSet.get(i) && (testNodeRef != null) && (readCheck != AccessDecisionVoter.ACCESS_GRANTED)) + { + incudedSet.set(i, false); + } + + } + } + + if (incudedSet.cardinality() == returnedObject.length) + { + return returnedObject; + } + else + { + Object[] answer = new Object[incudedSet.cardinality()]; + for (int i = incudedSet.nextSetBit(0), p = 0; i >= 0; i = incudedSet.nextSetBit(++i), p++) + { + answer[p] = returnedObject[i]; + } + return answer; + } + } + + private NodeRef getParentReadCheckNode(Object current) + { + NodeRef testNodeRef = null; + if (StoreRef.class.isAssignableFrom(current.getClass())) + { + testNodeRef = null; + } + else if (NodeRef.class.isAssignableFrom(current.getClass())) + { + testNodeRef = nodeService.getPrimaryParent((NodeRef) current).getParentRef(); + } + else if (ChildAssociationRef.class.isAssignableFrom(current.getClass())) + { + testNodeRef = ((ChildAssociationRef) current).getParentRef(); + } + else if (PermissionCheckValue.class.isAssignableFrom(current.getClass())) + { + NodeRef nodeRef = ((PermissionCheckValue) current).getNodeRef(); + testNodeRef = nodeService.getPrimaryParent(nodeRef).getParentRef(); + } + else + { + throw new ACLEntryVoterException("The specified array is not of NodeRef or ChildAssociationRef"); + } + return testNodeRef; + } + + private NodeRef getChildReadCheckNode(Object current) + { + NodeRef testNodeRef = null; + if (StoreRef.class.isAssignableFrom(current.getClass())) + { + testNodeRef = nodeService.getRootNode((StoreRef) current); + } + else if (NodeRef.class.isAssignableFrom(current.getClass())) + { + testNodeRef = (NodeRef) current; + } + else if (ChildAssociationRef.class.isAssignableFrom(current.getClass())) + { + testNodeRef = ((ChildAssociationRef) current).getChildRef(); + } + else if (PermissionCheckValue.class.isAssignableFrom(current.getClass())) + { + testNodeRef = ((PermissionCheckValue) current).getNodeRef(); + } + else + { + throw new ACLEntryVoterException("The specified array is not of NodeRef or ChildAssociationRef"); + } + return testNodeRef; + } + + @SuppressWarnings({"unchecked" }) + private Map decide(Authentication authentication, Object object, ConfigAttributeDefinition config, Map returnedObject) throws AccessDeniedException + { + if (returnedObject.containsKey(RecordsManagementModel.PROP_HOLD_REASON)) + { + HashMap filtered = new HashMap(); + filtered.putAll(returnedObject); + // get the node ref from the properties or delete + String protocol = DefaultTypeConverter.INSTANCE.convert(String.class, filtered.get(ContentModel.PROP_STORE_PROTOCOL)); + String identifier = DefaultTypeConverter.INSTANCE.convert(String.class, filtered.get(ContentModel.PROP_STORE_IDENTIFIER)); + String uuid = DefaultTypeConverter.INSTANCE.convert(String.class, filtered.get(ContentModel.PROP_NODE_UUID)); + StoreRef storeRef = new StoreRef(protocol, identifier); + NodeRef nodeRef = new NodeRef(storeRef, uuid); + if ((nodeRef == null) || (permissionService.hasPermission(rmService.getFilePlan(nodeRef), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE) != AccessStatus.ALLOWED)) + { + filtered.remove(RecordsManagementModel.PROP_HOLD_REASON); + } + return filtered; + } + else + { + return returnedObject; + } + } + + private class ConfigAttributeDefintion + { + + String typeString; + + String mode; + + boolean parent = false; + + ConfigAttributeDefintion(ConfigAttribute attr) + { + + StringTokenizer st = new StringTokenizer(attr.getAttribute(), ".", false); + typeString = st.nextToken(); + if (!(typeString.equals(AFTER_RM))) + { + throw new ACLEntryVoterException("Invalid type: must be AFTER_RM"); + } + mode = st.nextToken(); + + if (st.hasMoreElements()) + { + parent = true; + } + } + } + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMEntryVoter.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMEntryVoter.java new file mode 100644 index 0000000000..dd8549c68c --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMEntryVoter.java @@ -0,0 +1,1112 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability; + +import java.io.Serializable; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; + +import net.sf.acegisecurity.Authentication; +import net.sf.acegisecurity.ConfigAttribute; +import net.sf.acegisecurity.ConfigAttributeDefinition; +import net.sf.acegisecurity.vote.AccessDecisionVoter; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction; +import org.alfresco.module.org_alfresco_module_rm.capability.impl.CreateCapability; +import org.alfresco.module.org_alfresco_module_rm.capability.impl.MoveRecordsCapability; +import org.alfresco.module.org_alfresco_module_rm.capability.impl.UpdateCapability; +import org.alfresco.module.org_alfresco_module_rm.capability.impl.UpdatePropertiesCapability; +import org.alfresco.module.org_alfresco_module_rm.caveat.RMCaveatConfigComponent; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.permissions.impl.SimplePermissionReference; +import org.alfresco.repo.security.permissions.impl.acegi.ACLEntryVoterException; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.security.OwnableService; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.EqualsHelper; +import org.aopalliance.intercept.MethodInvocation; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +public class RMEntryVoter extends RMSecurityCommon + implements AccessDecisionVoter, InitializingBean, ApplicationContextAware +{ + private static Log logger = LogFactory.getLog(RMEntryVoter.class); + + private static final String RM = "RM"; + private static final String RM_ALLOW = "RM_ALLOW"; + private static final String RM_DENY = "RM_DENY"; + private static final String RM_CAP = "RM_CAP"; + private static final String RM_ABSTAIN = "RM_ABSTAIN"; + private static final String RM_QUERY = "RM_QUERY"; + + private NamespacePrefixResolver nspr; + private NodeService nodeService; + private PermissionService permissionService; + private RMCaveatConfigComponent caveatConfigComponent; + private DictionaryService dictionaryService; + private RecordsManagementService recordsManagementService; + private DispositionService dispositionService; + private SearchService searchService; + private OwnableService ownableService; + + private CapabilityService capabilityService; + + private static HashMap policies = new HashMap(); + + private HashSet protectedProperties = new HashSet(); + + private HashSet protectedAspects = new HashSet(); + + + static + { + policies.put("Read", new ReadPolicy()); + policies.put("Create", new CreatePolicy()); + policies.put("Move", new MovePolicy()); + policies.put("Update", new UpdatePolicy()); + policies.put("Delete", new DeletePolicy()); + policies.put("UpdateProperties", new UpdatePropertiesPolicy()); + policies.put("Assoc", new AssocPolicy()); + policies.put("WriteContent", new WriteContentPolicy()); + policies.put("Capability", new CapabilityPolicy()); + policies.put("Declare", new DeclarePolicy()); + policies.put("ReadProperty", new ReadPropertyPolicy()); + + // restrictedProperties.put(RecordsManagementModel.PROP_IS_CLOSED, value) + + } + + /** + * Set the permission service + * + * @param permissionService + */ + public void setPermissionService(PermissionService permissionService) + { + this.permissionService = permissionService; + } + + /** + * Set the node service + * + * @param nodeService + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @param capabilityService capability service + */ + public void setCapabilityService(CapabilityService capabilityService) + { + this.capabilityService = capabilityService; + } + + /** + * Get the search service + * @return search service + */ + public SearchService getSearchService() + { + if (searchService == null) + { + searchService = (SearchService)applicationContext.getBean("SearchService"); + } + return searchService; + } + + /** + * @return + */ + public OwnableService getOwnableService() + { + if (ownableService == null) + { + ownableService = (OwnableService)applicationContext.getBean("ownableService"); + } + return ownableService; + } + + /** + * Set the name space prefix resolver + * + * @param nspr + */ + public void setNamespacePrefixResolver(NamespacePrefixResolver nspr) + { + this.nspr = nspr; + } + + public void setCaveatConfigComponent(RMCaveatConfigComponent caveatConfigComponent) + { + this.caveatConfigComponent = caveatConfigComponent; + } + + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + public boolean supports(ConfigAttribute attribute) + { + if ((attribute.getAttribute() != null) + && (attribute.getAttribute().equals(RM_ABSTAIN) + || attribute.getAttribute().equals(RM_QUERY) || attribute.getAttribute().equals(RM_ALLOW) || attribute.getAttribute().equals(RM_DENY) + || attribute.getAttribute().startsWith(RM_CAP) || attribute.getAttribute().startsWith(RM))) + { + return true; + } + else + { + return false; + } + } + + @SuppressWarnings("unchecked") + public boolean supports(Class clazz) + { + return (MethodInvocation.class.isAssignableFrom(clazz)); + } + + public void addProtectedProperties(Set properties) + { + protectedProperties.addAll(properties); + } + + public void addProtectedAspects(Set aspects) + { + protectedAspects.addAll(aspects); + } + + public Set getProtectedProperties() + { + return Collections.unmodifiableSet(protectedProperties); + } + + public Set getProtetcedAscpects() + { + return Collections.unmodifiableSet(protectedAspects); + } + + @SuppressWarnings("unchecked") + public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) + { + if (logger.isDebugEnabled()) + { + MethodInvocation mi = (MethodInvocation) object; + logger.debug("Method: " + mi.getMethod().toString()); + } + // The system user can do anything + if (AuthenticationUtil.isRunAsUserTheSystemUser()) + { + if (logger.isDebugEnabled()) + { + logger.debug("Access granted for the system user"); + } + return AccessDecisionVoter.ACCESS_GRANTED; + } + + List supportedDefinitions = extractSupportedDefinitions(config); + + // No RM definitions so we do not vote + if (supportedDefinitions.size() == 0) + { + return AccessDecisionVoter.ACCESS_ABSTAIN; + } + + MethodInvocation invocation = (MethodInvocation) object; + + Method method = invocation.getMethod(); + Class[] params = method.getParameterTypes(); + + // If there are only capability (RM_CAP) and policy (RM) entries non must deny + // If any abstain we deny + // All present must vote to allow unless an explicit direction comes first (e.g. RM_ALLOW) + + for (ConfigAttributeDefintion cad : supportedDefinitions) + { + // Whatever is found first takes precedence + if (cad.typeString.equals(RM_DENY)) + { + return AccessDecisionVoter.ACCESS_DENIED; + } + else if (cad.typeString.equals(RM_ABSTAIN)) + { + return AccessDecisionVoter.ACCESS_ABSTAIN; + } + else if (cad.typeString.equals(RM_ALLOW)) + { + return AccessDecisionVoter.ACCESS_GRANTED; + } + // RM_QUERY is a special case - the entry is allowed and filtering sorts out the results + // It is distinguished from RM_ALLOW so query may have additional behaviour in the future + else if (cad.typeString.equals(RM_QUERY)) + { + return AccessDecisionVoter.ACCESS_GRANTED; + } + // Ignore config that references method arguments that do not exist + // Arguably we should deny here but that requires a full impact analysis + // These entries effectively abstain + else if (((cad.parameters.get(0) != null) && (cad.parameters.get(0) >= invocation.getArguments().length)) + || ((cad.parameters.get(1) != null) && (cad.parameters.get(1) >= invocation.getArguments().length))) + { + continue; + } + else if (cad.typeString.equals(RM_CAP)) + { + switch(checkCapability(invocation, params, cad)) + { + case AccessDecisionVoter.ACCESS_DENIED: + return AccessDecisionVoter.ACCESS_DENIED; + case AccessDecisionVoter.ACCESS_ABSTAIN: + if(logger.isDebugEnabled()) + { + if(logger.isTraceEnabled()) + { + logger.trace("Capability " + cad.required + " abstained for " + invocation.getMethod(), new IllegalStateException()); + } + else + { + logger.debug("Capability " + cad.required + " abstained for " + invocation.getMethod()); + } + } + // abstain denies + return AccessDecisionVoter.ACCESS_DENIED; + case AccessDecisionVoter.ACCESS_GRANTED: + break; + } + } + else if (cad.typeString.equals(RM)) + { + switch(checkPolicy(invocation, params, cad)) + { + case AccessDecisionVoter.ACCESS_DENIED: + return AccessDecisionVoter.ACCESS_DENIED; + case AccessDecisionVoter.ACCESS_ABSTAIN: + if(logger.isDebugEnabled()) + { + if(logger.isTraceEnabled()) + { + logger.trace("Policy " + cad.policyName + " abstained for " + invocation.getMethod(), new IllegalStateException()); + } + else + { + logger.debug("Policy " + cad.policyName + " abstained for " + invocation.getMethod()); + } + } + // abstain denies + return AccessDecisionVoter.ACCESS_DENIED; + case AccessDecisionVoter.ACCESS_GRANTED: + break; + } + } + } + + // all voted to allow + + return AccessDecisionVoter.ACCESS_GRANTED; + + } + + @SuppressWarnings("unchecked") + private int checkCapability(MethodInvocation invocation, Class[] params, ConfigAttributeDefintion cad) + { + NodeRef testNodeRef = getTestNode(getNodeService(), getRecordsManagementService(), invocation, params, cad.parameters.get(0), cad.parent); + if (testNodeRef == null) + { + return AccessDecisionVoter.ACCESS_ABSTAIN; + } + Capability capability = capabilityService.getCapability(cad.required.getName()); + if (capability == null) + { + return AccessDecisionVoter.ACCESS_DENIED; + } + return capability.hasPermissionRaw(testNodeRef); + + } + + @SuppressWarnings("unchecked") + private static QName getType(NodeService nodeService, MethodInvocation invocation, Class[] params, int position, boolean parent) + { + if (QName.class.isAssignableFrom(params[position])) + { + if (invocation.getArguments()[position] != null) + { + QName qname = (QName) invocation.getArguments()[position]; + return qname; + } + } + else if (NodeRef.class.isAssignableFrom(params[position])) + { + if (invocation.getArguments()[position] != null) + { + NodeRef nodeRef = (NodeRef) invocation.getArguments()[position]; + return nodeService.getType(nodeRef); + } + } + + return null; + } + + @SuppressWarnings("unchecked") + private static QName getQName(MethodInvocation invocation, Class[] params, int position) + { + if (QName.class.isAssignableFrom(params[position])) + { + if (invocation.getArguments()[position] != null) + { + QName qname = (QName) invocation.getArguments()[position]; + return qname; + } + } + throw new ACLEntryVoterException("Unknown type"); + } + + @SuppressWarnings("unchecked") + private static Serializable getProperty(MethodInvocation invocation, Class[] params, int position) + { + if (invocation.getArguments()[position] == null) + { + return null; + } + if (Serializable.class.isAssignableFrom(params[position])) + { + if (invocation.getArguments()[position] != null) + { + Serializable property = (Serializable) invocation.getArguments()[position]; + return property; + } + } + throw new ACLEntryVoterException("Unknown type"); + } + + @SuppressWarnings("unchecked") + private static Map getProperties(MethodInvocation invocation, Class[] params, int position) + { + if (invocation.getArguments()[position] == null) + { + return null; + } + if (Map.class.isAssignableFrom(params[position])) + { + if (invocation.getArguments()[position] != null) + { + Map properties = (Map) invocation.getArguments()[position]; + return properties; + } + } + throw new ACLEntryVoterException("Unknown type"); + } + + @SuppressWarnings("unchecked") + private static NodeRef getTestNode(NodeService nodeService, RecordsManagementService rmService, MethodInvocation invocation, Class[] params, int position, boolean parent) + { + NodeRef testNodeRef = null; + if (position < 0) + { + // Test against the fileplan root node + List rmRoots = rmService.getFilePlans(); + if (rmRoots.size() != 0) + { + // TODO for now we can take the first one as we only support a single rm site + testNodeRef = rmRoots.get(0); + + if (logger.isDebugEnabled()) + { + logger.debug("\tPermission test against the rm root node " + nodeService.getPath(testNodeRef)); + } + } + } + else if (StoreRef.class.isAssignableFrom(params[position])) + { + if (invocation.getArguments()[position] != null) + { + if (logger.isDebugEnabled()) + { + logger.debug("\tPermission test against the store - using permissions on the root node"); + } + StoreRef storeRef = (StoreRef) invocation.getArguments()[position]; + if (nodeService.exists(storeRef)) + { + testNodeRef = nodeService.getRootNode(storeRef); + } + } + } + else if (NodeRef.class.isAssignableFrom(params[position])) + { + testNodeRef = (NodeRef) invocation.getArguments()[position]; + if (parent) + { + testNodeRef = nodeService.getPrimaryParent(testNodeRef).getParentRef(); + if (logger.isDebugEnabled()) + { + if (nodeService.exists(testNodeRef)) + { + logger.debug("\tPermission test for parent on node " + nodeService.getPath(testNodeRef)); + } + else + { + logger.debug("\tPermission test for parent on non-existing node " + testNodeRef); + } + logger.debug("\tPermission test for parent on node " + nodeService.getPath(testNodeRef)); + } + } + else + { + if (logger.isDebugEnabled()) + { + if (nodeService.exists(testNodeRef)) + { + logger.debug("\tPermission test on node " + nodeService.getPath(testNodeRef)); + } + else + { + logger.debug("\tPermission test on non-existing node " + testNodeRef); + } + } + } + } + else if (ChildAssociationRef.class.isAssignableFrom(params[position])) + { + if (invocation.getArguments()[position] != null) + { + if (parent) + { + testNodeRef = ((ChildAssociationRef) invocation.getArguments()[position]).getParentRef(); + } + else + { + testNodeRef = ((ChildAssociationRef) invocation.getArguments()[position]).getChildRef(); + } + if (logger.isDebugEnabled()) + { + if (nodeService.exists(testNodeRef)) + { + logger.debug("\tPermission test on node " + nodeService.getPath(testNodeRef)); + } + else + { + logger.debug("\tPermission test on non-existing node " + testNodeRef); + } + } + } + } + else if (AssociationRef.class.isAssignableFrom(params[position])) + { + if (invocation.getArguments()[position] != null) + { + if (parent) + { + testNodeRef = ((AssociationRef) invocation.getArguments()[position]).getSourceRef(); + } + else + { + testNodeRef = ((AssociationRef) invocation.getArguments()[position]).getTargetRef(); + } + if (logger.isDebugEnabled()) + { + if (nodeService.exists(testNodeRef)) + { + logger.debug("\tPermission test on node " + nodeService.getPath(testNodeRef)); + } + else + { + logger.debug("\tPermission test on non-existing node " + testNodeRef); + } + } + } + } + return testNodeRef; + } + + @SuppressWarnings("unchecked") + private int checkPolicy(MethodInvocation invocation, Class[] params, ConfigAttributeDefintion cad) + { + Policy policy = policies.get(cad.policyName); + if (policy == null) + { + return AccessDecisionVoter.ACCESS_GRANTED; + } + else + { + return policy.evaluate(this.nodeService, this.recordsManagementService, this.capabilityService, invocation, params, cad); + } + } + + public void afterPropertiesSet() throws Exception + { + // TODO Auto-generated method stub + + } + + @SuppressWarnings("unchecked") + private List extractSupportedDefinitions(ConfigAttributeDefinition config) + { + List definitions = new ArrayList(2); + Iterator iter = config.getConfigAttributes(); + + while (iter.hasNext()) + { + ConfigAttribute attr = (ConfigAttribute) iter.next(); + + if (this.supports(attr)) + { + definitions.add(new ConfigAttributeDefintion(attr)); + } + + } + return definitions; + } + + /** + * @return the nodeService + */ + public NodeService getNodeService() + { + return nodeService; + } + + /** + * @return the permissionService + */ + public PermissionService getPermissionService() + { + return permissionService; + } + + /** + * @return the caveatConfigService + */ + public RMCaveatConfigComponent getCaveatConfigComponent() + { + return caveatConfigComponent; + } + + /** + * @param recordsManagementService + * the recordsManagementService to set + */ + public void setRecordsManagementService(RecordsManagementService recordsManagementService) + { + this.recordsManagementService = recordsManagementService; + } + + /** + * @return the recordsManagementService + */ + public RecordsManagementService getRecordsManagementService() + { + return recordsManagementService; + } + + public void setDispositionService(DispositionService dispositionService) + { + this.dispositionService = dispositionService; + } + + public DispositionService getDispositionService() + { + return dispositionService; + } + + /** + * @return the dictionaryService + */ + public DictionaryService getDictionaryService() + { + return dictionaryService; + } + + public boolean isProtectedAspect(NodeRef nodeRef, QName aspectQName) + { + if(protectedAspects.contains(aspectQName)) + { + for(Capability capability : capabilityService.getCapabilities()) + { + for(RecordsManagementAction action : capability.getActions()) + { + if(action.getProtectedAspects().contains(aspectQName)) + { + if(action.isExecutable(nodeRef, null)) + { + return false; + } + } + } + } + return true; + } + else + { + return false; + } + } + + public boolean isProtectedProperty(NodeRef nodeRef, QName propertyQName) + { + if(protectedProperties.contains(propertyQName)) + { + for(Capability capability : capabilityService.getCapabilities()) + { + for(RecordsManagementAction action : capability.getActions()) + { + if(action.getProtectedProperties().contains(propertyQName)) + { + if(action.isExecutable(nodeRef, null)) + { + return false; + } + } + } + } + return true; + } + else + { + return false; + } + } + + public boolean includesProtectedPropertyChange(NodeRef nodeRef, Map properties) + { + Map originals = nodeService.getProperties(nodeRef); + for (QName test : properties.keySet()) + { + if (isProtectedProperty(nodeRef, test)) + { + if (!EqualsHelper.nullSafeEquals(originals.get(test), properties.get(test))) + { + return true; + } + } + } + return false; + } + + private class ConfigAttributeDefintion + { + String typeString; + + String policyName; + + SimplePermissionReference required; + + HashMap parameters = new HashMap(2, 1.0f); + + boolean parent = false; + + ConfigAttributeDefintion(ConfigAttribute attr) + { + StringTokenizer st = new StringTokenizer(attr.getAttribute(), ".", false); + if (st.countTokens() < 1) + { + throw new ACLEntryVoterException("There must be at least one token in a config attribute"); + } + typeString = st.nextToken(); + + if (!(typeString.equals(RM) || typeString.equals(RM_ALLOW) || typeString.equals(RM_CAP) || typeString.equals(RM_DENY) || typeString.equals(RM_QUERY) || typeString + .equals(RM_ABSTAIN))) + { + throw new ACLEntryVoterException("Invalid type: must be ACL_NODE, ACL_PARENT or ACL_ALLOW"); + } + + if (typeString.equals(RM)) + { + policyName = st.nextToken(); + int position = 0; + while (st.hasMoreElements()) + { + String numberString = st.nextToken(); + Integer value = Integer.parseInt(numberString); + parameters.put(position, value); + position++; + } + } + else if (typeString.equals(RM_CAP)) + { + String numberString = st.nextToken(); + String qNameString = st.nextToken(); + String permissionString = st.nextToken(); + + Integer value = Integer.parseInt(numberString); + parameters.put(0, value); + + QName qName = QName.createQName(qNameString, nspr); + + required = SimplePermissionReference.getPermissionReference(qName, permissionString); + + if (st.hasMoreElements()) + { + parent = true; + } + } + } + } + + interface Policy + { + /** + * + * @param nodeService + * @param rmService + * @param capabilitiesService + * @param invocation + * @param params + * @param cad + * @return + */ + @SuppressWarnings("unchecked") + int evaluate( + NodeService nodeService, + RecordsManagementService rmService, + CapabilityService capabilitiesService, + MethodInvocation invocation, + Class[] params, + ConfigAttributeDefintion cad); + } + + private static class ReadPolicy implements Policy + { + + @SuppressWarnings("unchecked") + public int evaluate( + NodeService nodeService, + RecordsManagementService rmService, + CapabilityService capabilityService, + MethodInvocation invocation, + Class[] params, + ConfigAttributeDefintion cad) + { + NodeRef testNodeRef = getTestNode(nodeService, rmService, invocation, params, cad.parameters.get(0), cad.parent); + return capabilityService.getCapability(RMPermissionModel.VIEW_RECORDS).evaluate(testNodeRef); + } + + } + + private static class CreatePolicy implements Policy + { + + @SuppressWarnings("unchecked") + public int evaluate( + NodeService nodeService, + RecordsManagementService rmService, + CapabilityService capabilityService, + MethodInvocation invocation, + Class[] params, + ConfigAttributeDefintion cad) + { + + NodeRef destination = getTestNode(nodeService, rmService, invocation, params, cad.parameters.get(0), cad.parent); + QName type = getType(nodeService, invocation, params, cad.parameters.get(1), cad.parent); + // linkee is not null for creating secondary child assocs + NodeRef linkee = getTestNode(nodeService, rmService, invocation, params, cad.parameters.get(1), cad.parent); + QName assocType = null; + if(cad.parameters.size() > 2) + { + assocType = getType(nodeService, invocation, params, cad.parameters.get(2), cad.parent); + } + + return ((CreateCapability)capabilityService.getCapability("Create")).evaluate(destination, linkee, type, assocType); + } + + } + + private static class MovePolicy implements Policy + { + + @SuppressWarnings("unchecked") + public int evaluate( + NodeService nodeService, + RecordsManagementService rmService, + CapabilityService capabilityService, + MethodInvocation invocation, + Class[] params, + ConfigAttributeDefintion cad) + { + + NodeRef movee = null; + if (cad.parameters.get(0) > -1) + { + movee = getTestNode(nodeService, rmService, invocation, params, cad.parameters.get(0), cad.parent); + } + + NodeRef destination = null; + if (cad.parameters.get(1) > -1) + { + destination = getTestNode(nodeService, rmService, invocation, params, cad.parameters.get(1), cad.parent); + } + + if ((movee != null) && (destination != null)) + { + return ((MoveRecordsCapability)capabilityService.getCapability(RMPermissionModel.MOVE_RECORDS)).evaluate(movee, destination); + } + else + { + return AccessDecisionVoter.ACCESS_DENIED; + } + + } + } + + private static class UpdatePolicy implements Policy + { + + @SuppressWarnings("unchecked") + public int evaluate( + NodeService nodeService, + RecordsManagementService rmService, + CapabilityService capabilityService, + MethodInvocation invocation, + Class[] params, + ConfigAttributeDefintion cad) + { + NodeRef updatee = getTestNode(nodeService, rmService, invocation, params, cad.parameters.get(0), cad.parent); + QName aspectQName = null; + if (cad.parameters.size() > 1) + { + if (cad.parameters.get(1) > -1) + { + aspectQName = getQName(invocation, params, cad.parameters.get(1)); + } + } + Map properties = null; + if (cad.parameters.size() > 2) + { + if (cad.parameters.get(2) > -1) + { + properties = getProperties(invocation, params, cad.parameters.get(2)); + } + } + + UpdateCapability updateCapability = (UpdateCapability)capabilityService.getCapability("Update"); + return updateCapability.evaluate(updatee, aspectQName, properties); + } + + } + + private static class DeletePolicy implements Policy + { + + @SuppressWarnings("unchecked") + public int evaluate( + NodeService nodeService, + RecordsManagementService rmService, + CapabilityService capabilityService, + MethodInvocation invocation, + Class[] params, + ConfigAttributeDefintion cad) + { + NodeRef deletee = null; + if (cad.parameters.get(0) > -1) + { + deletee = getTestNode(nodeService, rmService, invocation, params, cad.parameters.get(0), cad.parent); + } + if (deletee != null) + { + + return capabilityService.getCapability("Delete").evaluate(deletee); + + } + else + { + return AccessDecisionVoter.ACCESS_DENIED; + } + } + + } + + private static class UpdatePropertiesPolicy implements Policy + { + @SuppressWarnings("unchecked") + public int evaluate( + NodeService nodeService, + RecordsManagementService rmService, + CapabilityService capabilityService, + MethodInvocation invocation, + Class[] params, + ConfigAttributeDefintion cad) + { + NodeRef updatee = getTestNode(nodeService, rmService, invocation, params, cad.parameters.get(0), cad.parent); + Map properties; + if (QName.class.isAssignableFrom(params[cad.parameters.get(1)])) + { + // single update/delete + // We have a specific property + QName propertyQName = getQName(invocation, params, cad.parameters.get(1)); + properties = new HashMap(1, 1.0f); + if (cad.parameters.size() > 2) + { + properties.put(propertyQName, getProperty(invocation, params, cad.parameters.get(2))); + } + else + { + properties.put(propertyQName, null); + } + } + else + { + properties = getProperties(invocation, params, cad.parameters.get(1)); + } + + return ((UpdatePropertiesCapability)capabilityService.getCapability("UpdateProperties")).evaluate(updatee, properties); + } + + } + + private static class AssocPolicy implements Policy + { + + @SuppressWarnings("unchecked") + public int evaluate( + NodeService nodeService, + RecordsManagementService rmService, + CapabilityService capabilityService, + MethodInvocation invocation, + Class[] params, + ConfigAttributeDefintion cad) + { + Policy policy = policies.get("Read"); + if (policy == null) + { + return AccessDecisionVoter.ACCESS_DENIED; + } + else + { + return policy.evaluate(nodeService, rmService, capabilityService, invocation, params, cad); + } + } + + } + + private static class WriteContentPolicy implements Policy + { + + @SuppressWarnings("unchecked") + public int evaluate( + NodeService nodeService, + RecordsManagementService rmService, + CapabilityService capabilityService, + MethodInvocation invocation, + Class[] params, + ConfigAttributeDefintion cad) + { + NodeRef updatee = getTestNode(nodeService, rmService, invocation, params, cad.parameters.get(0), cad.parent); + return capabilityService.getCapability("WriteContent").evaluate(updatee); + } + + } + + private static class CapabilityPolicy implements Policy + { + + @SuppressWarnings("unchecked") + public int evaluate( + NodeService nodeService, + RecordsManagementService rmService, + CapabilityService capabilityService, + MethodInvocation invocation, + Class[] params, + ConfigAttributeDefintion cad) + { + NodeRef assignee = getTestNode(nodeService, rmService, invocation, params, cad.parameters.get(0), cad.parent); + return capabilityService.getCapability(RMPermissionModel.MANAGE_ACCESS_CONTROLS).evaluate(assignee); + } + + } + + private static class DeclarePolicy implements Policy + { + + @SuppressWarnings("unchecked") + public int evaluate( + NodeService nodeService, + RecordsManagementService rmService, + CapabilityService capabilityService, + MethodInvocation invocation, + Class[] params, + ConfigAttributeDefintion cad) + { + NodeRef declaree = getTestNode(nodeService, rmService, invocation, params, cad.parameters.get(0), cad.parent); + return capabilityService.getCapability("Declare").evaluate(declaree); + } + + } + + private static class ReadPropertyPolicy implements Policy + { + + @SuppressWarnings("unchecked") + public int evaluate( + NodeService nodeService, + RecordsManagementService rmService, + CapabilityService capabilityService, + MethodInvocation invocation, + Class[] params, + ConfigAttributeDefintion cad) + { + NodeRef nodeRef = getTestNode(nodeService, rmService, invocation, params, cad.parameters.get(0), cad.parent); + QName propertyQName = getQName(invocation, params, cad.parameters.get(1)); + if(propertyQName.equals(RecordsManagementModel.PROP_HOLD_REASON)) + { + return capabilityService.getCapability(RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE).evaluate(nodeRef); + } + else + { + return AccessDecisionVoter.ACCESS_GRANTED; + } + } + + } + + private ApplicationContext applicationContext; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException + { + this.applicationContext = applicationContext; + } + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMPermissionModel.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMPermissionModel.java new file mode 100644 index 0000000000..5dafa88c16 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMPermissionModel.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability; + +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.security.permissions.impl.SimplePermissionReference; + +/** + * Capability constants for the RM Permission Model + * + * @author andyh + */ +public interface RMPermissionModel +{ + // Assignment of Filing + + public static final String FILING = "Filing"; + + public static final String READ_RECORDS = "ReadRecords"; + + public static final String FILE_RECORDS = "FileRecords"; + + // Roles + + public static final String ROLE_NAME_USER = "User"; + public static final String ROLE_USER = SimplePermissionReference.getPermissionReference(RecordsManagementModel.ASPECT_FILE_PLAN_COMPONENT, ROLE_NAME_USER).toString(); + + public static final String ROLE_NAME_POWER_USER = "PowerUser"; + public static final String ROLE_POWER_USER = SimplePermissionReference.getPermissionReference(RecordsManagementModel.ASPECT_FILE_PLAN_COMPONENT, ROLE_NAME_POWER_USER).toString(); + + public static final String ROLE_NAME_SECURITY_OFFICER = "SecurityOfficer"; + public static final String ROLE_SECURITY_OFFICER = SimplePermissionReference.getPermissionReference(RecordsManagementModel.ASPECT_FILE_PLAN_COMPONENT, ROLE_NAME_SECURITY_OFFICER) + .toString(); + + public static final String ROLE_NAME_RECORDS_MANAGER = "RecordsManager"; + public static final String ROLE_RECORDS_MANAGER = SimplePermissionReference.getPermissionReference(RecordsManagementModel.ASPECT_FILE_PLAN_COMPONENT, ROLE_NAME_RECORDS_MANAGER) + .toString(); + + public static final String ROLE_NAME_ADMINISTRATOR = "Administrator"; + public static final String ROLE_ADMINISTRATOR = SimplePermissionReference.getPermissionReference(RecordsManagementModel.ASPECT_FILE_PLAN_COMPONENT, ROLE_NAME_ADMINISTRATOR).toString(); + + // Capability permissions + + public static final String DECLARE_RECORDS = "DeclareRecords"; + + public static final String VIEW_RECORDS = "ViewRecords"; + + public static final String CREATE_MODIFY_DESTROY_FOLDERS = "CreateModifyDestroyFolders"; + + public static final String EDIT_RECORD_METADATA = "EditRecordMetadata"; + + public static final String EDIT_NON_RECORD_METADATA = "EditNonRecordMetadata"; + + public static final String ADD_MODIFY_EVENT_DATES = "AddModifyEventDates"; + + public static final String CLOSE_FOLDERS = "CloseFolders"; + + public static final String DECLARE_RECORDS_IN_CLOSED_FOLDERS = "DeclareRecordsInClosedFolders"; + + public static final String RE_OPEN_FOLDERS = "ReOpenFolders"; + + public static final String CYCLE_VITAL_RECORDS = "CycleVitalRecords"; + + public static final String PLANNING_REVIEW_CYCLES = "PlanningReviewCycles"; + + public static final String UPDATE_TRIGGER_DATES = "UpdateTriggerDates"; + + public static final String CREATE_MODIFY_DESTROY_EVENTS = "CreateModifyDestroyEvents"; + + public static final String MANAGE_ACCESS_RIGHTS = "ManageAccessRights"; + + public static final String MOVE_RECORDS = "MoveRecords"; + + public static final String CHANGE_OR_DELETE_REFERENCES = "ChangeOrDeleteReferences"; + + public static final String DELETE_LINKS = "DeleteLinks"; + + public static final String EDIT_DECLARED_RECORD_METADATA = "EditDeclaredRecordMetadata"; + + public static final String MANUALLY_CHANGE_DISPOSITION_DATES = "ManuallyChangeDispositionDates"; + + public static final String APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF = "ApproveRecordsScheduledForCutoff"; + + public static final String CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS = "CreateModifyRecordsInCutoffFolders"; + + public static final String EXTEND_RETENTION_PERIOD_OR_FREEZE = "ExtendRetentionPeriodOrFreeze"; + + public static final String UNFREEZE = "Unfreeze"; + + public static final String VIEW_UPDATE_REASONS_FOR_FREEZE = "ViewUpdateReasonsForFreeze"; + + public static final String DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION = "DestroyRecordsScheduledForDestruction"; + + public static final String DESTROY_RECORDS = "DestroyRecords"; + + public static final String UPDATE_VITAL_RECORD_CYCLE_INFORMATION = "UpdateVitalRecordCycleInformation"; + + public static final String UNDECLARE_RECORDS = "UndeclareRecords"; + + public static final String DECLARE_AUDIT_AS_RECORD = "DeclareAuditAsRecord"; + + public static final String DELETE_AUDIT = "DeleteAudit"; + + public static final String CREATE_MODIFY_DESTROY_TIMEFRAMES = "CreateModifyDestroyTimeframes"; + + public static final String AUTHORIZE_NOMINATED_TRANSFERS = "AuthorizeNominatedTransfers"; + + public static final String EDIT_SELECTION_LISTS = "EditSelectionLists"; + + public static final String AUTHORIZE_ALL_TRANSFERS = "AuthorizeAllTransfers"; + + public static final String CREATE_MODIFY_DESTROY_FILEPLAN_METADATA = "CreateModifyDestroyFileplanMetadata"; + + public static final String CREATE_AND_ASSOCIATE_SELECTION_LISTS = "CreateAndAssociateSelectionLists"; + + public static final String ATTACH_RULES_TO_METADATA_PROPERTIES = "AttachRulesToMetadataProperties"; + + public static final String CREATE_MODIFY_DESTROY_FILEPLAN_TYPES = "CreateModifyDestroyFileplanTypes"; + + public static final String CREATE_MODIFY_DESTROY_RECORD_TYPES = "CreateModifyDestroyRecordTypes"; + + public static final String MAKE_OPTIONAL_PARAMETERS_MANDATORY = "MakeOptionalParametersMandatory"; + + public static final String MAP_EMAIL_METADATA = "MapEmailMetadata"; + + public static final String DELETE_RECORDS = "DeleteRecords"; + + public static final String TRIGGER_AN_EVENT = "TriggerAnEvent"; + + public static final String CREATE_MODIFY_DESTROY_ROLES = "CreateModifyDestroyRoles"; + + public static final String CREATE_MODIFY_DESTROY_USERS_AND_GROUPS = "CreateModifyDestroyUsersAndGroups"; + + public static final String PASSWORD_CONTROL = "PasswordControl"; + + public static final String ENABLE_DISABLE_AUDIT_BY_TYPES = "EnableDisableAuditByTypes"; + + public static final String SELECT_AUDIT_METADATA = "SelectAuditMetadata"; + + public static final String DISPLAY_RIGHTS_REPORT = "DisplayRightsReport"; + + public static final String ACCESS_AUDIT = "AccessAudit"; + + public static final String EXPORT_AUDIT = "ExportAudit"; + + public static final String CREATE_MODIFY_DESTROY_REFERENCE_TYPES = "CreateModifyDestroyReferenceTypes"; + + public static final String UPDATE_CLASSIFICATION_DATES = "UpdateClassificationDates"; + + public static final String CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES = "CreateModifyDestroyClassificationGuides"; + + public static final String UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS = "UpgradeDowngradeAndDeclassifyRecords"; + + public static final String UPDATE_EXEMPTION_CATEGORIES = "UpdateExemptionCategories"; + + public static final String MAP_CLASSIFICATION_GUIDE_METADATA = "MapClassificationGuideMetadata"; + + public static final String MANAGE_ACCESS_CONTROLS = "ManageAccessControls"; +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMSecurityCommon.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMSecurityCommon.java new file mode 100644 index 0000000000..fd58ad0463 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMSecurityCommon.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability; + +import net.sf.acegisecurity.vote.AccessDecisionVoter; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.caveat.RMCaveatConfigComponent; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.security.PermissionService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * @author Roy Wetherall + * @since 2.0 + */ +public class RMSecurityCommon +{ + protected int NOSET_VALUE = -100; + + private static Log logger = LogFactory.getLog(RMSecurityCommon.class); + + protected NodeService nodeService; + protected PermissionService permissionService; + protected RecordsManagementService rmService; + protected RMCaveatConfigComponent caveatConfigComponent; + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setPermissionService(PermissionService permissionService) + { + this.permissionService = permissionService; + } + + public void setRecordsManagementService(RecordsManagementService rmService) + { + this.rmService = rmService; + } + + public void setCaveatConfigComponent(RMCaveatConfigComponent caveatConfigComponent) + { + this.caveatConfigComponent = caveatConfigComponent; + } + + /** + * + * @param prefix + * @param nodeRef + * @param value + * @return + */ + protected int setTransactionCache(String prefix, NodeRef nodeRef, int value) + { + String user = AuthenticationUtil.getRunAsUser(); + AlfrescoTransactionSupport.bindResource(prefix + nodeRef.toString() + user, Integer.valueOf(value)); + return value; + } + + /** + * + * @param prefix + * @param nodeRef + * @return + */ + protected int getTransactionCache(String prefix, NodeRef nodeRef) + { + int result = NOSET_VALUE; + String user = AuthenticationUtil.getRunAsUser(); + Integer value = (Integer)AlfrescoTransactionSupport.getResource(prefix + nodeRef.toString() + user); + if (value != null) + { + result = value.intValue(); + } + return result; + } + + /** + * + * @param nodeRef + * @return + */ + public int checkRead(NodeRef nodeRef) + { + if (nodeRef != null) + { + // now we know the node - we can abstain for certain types and aspects (eg, rm) + return checkRead(nodeRef, false); + } + + return AccessDecisionVoter.ACCESS_ABSTAIN; + } + + /** + * + * @param nodeRef + * @param allowDMRead + * @return + */ + public int checkRead(NodeRef nodeRef, boolean allowDMRead) + { + if (nodeService.hasAspect(nodeRef, RecordsManagementModel.ASPECT_FILE_PLAN_COMPONENT)) + { + return checkRmRead(nodeRef); + } + else + { + if (allowDMRead) + { + // Check DM read for copy etc + // DM does not grant - it can only deny + if (permissionService.hasPermission(nodeRef, PermissionService.READ) == AccessStatus.DENIED) + { + if (logger.isDebugEnabled()) + { + logger.debug("\t\tPermission is denied"); + Thread.dumpStack(); + } + return AccessDecisionVoter.ACCESS_DENIED; + } + else + { + return AccessDecisionVoter.ACCESS_GRANTED; + } + } + else + { + return AccessDecisionVoter.ACCESS_ABSTAIN; + } + } + } + + /** + * + * @param nodeRef + * @return + */ + public int checkRmRead(NodeRef nodeRef) + { + int result = getTransactionCache("checkRmRead", nodeRef); + if (result != NOSET_VALUE) + { + return result; + } + + // Get the file plan for the node + NodeRef filePlan = rmService.getFilePlan(nodeRef); + + // Admin role + if (permissionService.hasPermission(filePlan, RMPermissionModel.ROLE_ADMINISTRATOR) == AccessStatus.ALLOWED) + { + if (logger.isDebugEnabled()) + { + logger.debug("\t\tAdmin access"); + Thread.dumpStack(); + } + return setTransactionCache("checkRmRead", nodeRef, AccessDecisionVoter.ACCESS_GRANTED); + } + + if (permissionService.hasPermission(nodeRef, RMPermissionModel.READ_RECORDS) == AccessStatus.DENIED) + { + if (logger.isDebugEnabled()) + { + logger.debug("\t\tPermission is denied"); + Thread.dumpStack(); + } + return setTransactionCache("checkRmRead", nodeRef, AccessDecisionVoter.ACCESS_DENIED); + } + + if (permissionService.hasPermission(filePlan, RMPermissionModel.VIEW_RECORDS) == AccessStatus.DENIED) + { + if (logger.isDebugEnabled()) + { + logger.debug("\t\tPermission is denied"); + Thread.dumpStack(); + } + return setTransactionCache("checkRmRead", nodeRef, AccessDecisionVoter.ACCESS_DENIED); + } + + if (caveatConfigComponent.hasAccess(nodeRef)) + { + return setTransactionCache("checkRmRead", nodeRef, AccessDecisionVoter.ACCESS_GRANTED); + } + else + { + return setTransactionCache("checkRmRead", nodeRef, AccessDecisionVoter.ACCESS_DENIED); + } + + } + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/AbstractCapabilityCondition.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/AbstractCapabilityCondition.java new file mode 100644 index 0000000000..f09769a026 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/AbstractCapabilityCondition.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability.declarative; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.PermissionService; +import org.springframework.beans.factory.BeanNameAware; + +/** + * Abstract capability condition. + * + * @author Roy Wetherall + */ +public abstract class AbstractCapabilityCondition implements CapabilityCondition, + BeanNameAware, + RecordsManagementModel +{ + /** Capability condition name */ + protected String name; + + /** Services */ + protected RecordsManagementService rmService; + protected PermissionService permissionService; + protected NodeService nodeService; + + /** + * @param rmService records management service + */ + public void setRecordsManagementService(RecordsManagementService rmService) + { + this.rmService = rmService; + } + + /** + * @param permissionService permission service + */ + public void setPermissionService(PermissionService permissionService) + { + this.permissionService = permissionService; + } + + /** + * @param nodeService node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.capability.declarative.CapabilityCondition#getName() + */ + @Override + public String getName() + { + return name; + } + + /** + * @see org.springframework.beans.factory.BeanNameAware#setBeanName(java.lang.String) + */ + @Override + public void setBeanName(String name) + { + this.name = name; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/CapabilityCondition.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/CapabilityCondition.java new file mode 100644 index 0000000000..08032bcd22 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/CapabilityCondition.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability.declarative; + +import org.alfresco.service.cmr.repository.NodeRef; + +public interface CapabilityCondition +{ + String getName(); + + boolean evaluate(NodeRef nodeRef); +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/CompositeCapability.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/CompositeCapability.java new file mode 100644 index 0000000000..56dfcd6922 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/CompositeCapability.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability.declarative; + +import java.util.List; + +import net.sf.acegisecurity.vote.AccessDecisionVoter; + +import org.alfresco.module.org_alfresco_module_rm.capability.Capability; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Generic implementation of a composite capability + * + * @author Roy Wetherall + */ +public class CompositeCapability extends DeclarativeCapability +{ + /** List of capabilities */ + private List capabilities; + + /** + * @param capabilites list of capabilities + */ + public void setCapabilities(List capabilities) + { + this.capabilities = capabilities; + } + + @Override + public int evaluateImpl(NodeRef nodeRef) + { + int result = AccessDecisionVoter.ACCESS_DENIED; + + // Check each capability using 'OR' logic + for (Capability capability : capabilities) + { + int capabilityResult = capability.evaluate(nodeRef); + if (capabilityResult == AccessDecisionVoter.ACCESS_GRANTED) + { + result = AccessDecisionVoter.ACCESS_GRANTED; + break; + } + } + + return result; + } + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/DeclarativeCapability.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/DeclarativeCapability.java new file mode 100644 index 0000000000..ad48640e9c --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/DeclarativeCapability.java @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability.declarative; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import net.sf.acegisecurity.vote.AccessDecisionVoter; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.module.org_alfresco_module_rm.FilePlanComponentKind; +import org.alfresco.module.org_alfresco_module_rm.capability.AbstractCapability; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.AccessStatus; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +/** + * Declarative capability implementation. + * + * @author Roy Wetherall + */ +public class DeclarativeCapability extends AbstractCapability implements ApplicationContextAware +{ + /** Application Context */ + protected ApplicationContext applicationContext; + + /** Required permissions */ + private List permissions; + + /** Map of conditions and expected evaluation result */ + private Map conditions; + + /** List of file plan component kinds one of which must be satisfied */ + private List kinds; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException + { + this.applicationContext = applicationContext; + } + + /** + * @param permissions permissions + */ + public void setPermissions(List permissions) + { + this.permissions = permissions; + } + + /** + * @param conditions conditions and expected values + */ + public void setConditions(Map conditions) + { + this.conditions = conditions; + } + + /** + * @return {@link Map} conditions and expected values + */ + public Map getConditions() + { + return conditions; + } + + /** + * @param kinds list of file plan component kinds that the + */ + public void setKinds(List kinds) + { + this.kinds = kinds; + } + + /** + * @return {@link List}<@link String> list of expected file plan component kinds + */ + public List getKinds() + { + return kinds; + } + + /** + * Helper @see #setPermissions(List) + * + * @param permission permission + */ + public void setPermission(String permission) + { + List permissions = new ArrayList(1); + permissions.add(permission); + this.permissions = permissions; + } + + /** + * Check the permissions passed. + * + * @param nodeRef node reference + * @return boolean true if the permissions are present, false otherwise + */ + protected boolean checkPermissionsImpl(NodeRef nodeRef, String ... permissions) + { + boolean result = true; + NodeRef filePlan = rmService.getFilePlan(nodeRef); + + for (String permission : permissions) + { + if (permissionService.hasPermission(filePlan, permission) != AccessStatus.ALLOWED) + { + result = false; + break; + } + } + + return result; + } + + /** + * Checks the permissions required for the capability. + * + * @param nodeRef + * @return + */ + protected boolean checkPermissions(NodeRef nodeRef) + { + boolean result = true; + if (permissions != null && permissions.isEmpty() == false) + { + result = checkPermissionsImpl(nodeRef, (String[])permissions.toArray(new String[permissions.size()])); + } + return result; + } + + /** + * Checks the passed conditions. + * + * @param nodeRef + * @return + */ + protected boolean checkConditions(NodeRef nodeRef, Map conditions) + { + boolean result = true; + if (conditions != null) + { + for (Map.Entry entry : conditions.entrySet()) + { + boolean expected = entry.getValue().booleanValue(); + String conditionName = entry.getKey(); + + CapabilityCondition condition = (CapabilityCondition)applicationContext.getBean(conditionName); + if (condition == null) + { + throw new AlfrescoRuntimeException("Capability condition " + conditionName + " does not exist. Check the configuration of the capability " + name + "."); + } + + boolean actual = condition.evaluate(nodeRef); + if (expected != actual) + { + result = false; + break; + } + } + + } + return result; + } + + /** + * Checks the set conditions. + * + * @param nodeRef node reference + * @return boolean true if conditions satisfied, false otherwise + */ + protected boolean checkConditions(NodeRef nodeRef) + { + return checkConditions(nodeRef, conditions); + } + + /** + * Checks that the node ref is of the expected kind + * + * @param nodeRef + * @return + */ + protected boolean checkKinds(NodeRef nodeRef) + { + boolean result = false; + + FilePlanComponentKind actualKind = rmService.getFilePlanComponentKind(nodeRef); + + if (actualKind != null) + { + if (kinds != null && kinds.isEmpty() == false) + { + // need to check the actual file plan kind is in the list specified + for (String kindString : kinds) + { + FilePlanComponentKind kind = FilePlanComponentKind.valueOf(kindString); + if (actualKind.equals(kind) == true) + { + result = true; + break; + } + } + } + else + { + // we don't have any specific kinds to check, so we pass since we have a file + // plan component in our hands + result = true; + } + } + + return result; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.capability.AbstractCapability#hasPermissionImpl(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public int evaluate(NodeRef nodeRef) + { + int result = AccessDecisionVoter.ACCESS_ABSTAIN; + + // Check we are dealing with a file plan component + if (rmService.isFilePlanComponent(nodeRef) == true) + { + // Check the kind of the object, the permissions and the conditions + if (checkKinds(nodeRef) == true && checkPermissions(nodeRef) == true && checkConditions(nodeRef) == true) + { + // Opportunity for child implementations to extend + result = evaluateImpl(nodeRef); + } + else + { + result = AccessDecisionVoter.ACCESS_DENIED; + } + } + + // Last chance for child implementations to veto/change the result + result = onEvaluate(nodeRef, result); + + return result; + } + + /** + * Default implementation. Given extending classes a hook point for further checks. + * + * @param nodeRef node reference + * @return + */ + protected int evaluateImpl(NodeRef nodeRef) + { + return AccessDecisionVoter.ACCESS_GRANTED; + } + + /** + * Default implementation. + * + * Called before evaluate completes. The result returned overwrites the already discovered result. + * Provides a hook point for child implementations that wish to veto the result. + * + * @param nodeRef + * @param result + * @return + */ + protected int onEvaluate(NodeRef nodeRef, int result) + { + return result; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/ClosedCapabilityCondition.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/ClosedCapabilityCondition.java new file mode 100644 index 0000000000..55d1b2756e --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/ClosedCapabilityCondition.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability.declarative.condition; + +import java.util.List; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.capability.declarative.AbstractCapabilityCondition; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.RegexQNamePattern; + +/** + * @author Roy Wetherall + */ +public class ClosedCapabilityCondition extends AbstractCapabilityCondition +{ + /** + * @see org.alfresco.module.org_alfresco_module_rm.capability.declarative.CapabilityCondition#evaluate(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public boolean evaluate(NodeRef nodeRef) + { + boolean result = false; + if (rmService.isRecordFolder(nodeRef) == true) + { + result = rmService.isRecordFolderClosed(nodeRef); + } + else if (rmService.isRecord(nodeRef) == true) + { + List assocs = nodeService.getParentAssocs(nodeRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef assoc : assocs) + { + if (rmService.isRecordFolderClosed(assoc.getParentRef()) == true) + { + result = true; + break; + } + } + } + return result; + } + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/CutoffCapabilityCondition.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/CutoffCapabilityCondition.java new file mode 100644 index 0000000000..37eb259e98 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/CutoffCapabilityCondition.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability.declarative.condition; + +import org.alfresco.module.org_alfresco_module_rm.capability.declarative.AbstractCapabilityCondition; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * @author Roy Wetherall + */ +public class CutoffCapabilityCondition extends AbstractCapabilityCondition +{ + /** + * @see org.alfresco.module.org_alfresco_module_rm.capability.declarative.CapabilityCondition#evaluate(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public boolean evaluate(NodeRef nodeRef) + { + return rmService.isCutoff(nodeRef); + } + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/DeclaredCapabilityCondition.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/DeclaredCapabilityCondition.java new file mode 100644 index 0000000000..b57801aab4 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/DeclaredCapabilityCondition.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability.declarative.condition; + +import org.alfresco.module.org_alfresco_module_rm.FilePlanComponentKind; +import org.alfresco.module.org_alfresco_module_rm.capability.declarative.AbstractCapabilityCondition; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * @author Roy Wetherall + */ +public class DeclaredCapabilityCondition extends AbstractCapabilityCondition +{ + @Override + public boolean evaluate(NodeRef nodeRef) + { + boolean result = false; + if (FilePlanComponentKind.RECORD.equals(rmService.getFilePlanComponentKind(nodeRef)) == true) + { + result = rmService.isRecordDeclared(nodeRef); + } + return result; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/DestroyedCapabilityCondition.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/DestroyedCapabilityCondition.java new file mode 100644 index 0000000000..f91fa9a47c --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/DestroyedCapabilityCondition.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability.declarative.condition; + +import org.alfresco.module.org_alfresco_module_rm.capability.declarative.AbstractCapabilityCondition; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Destroyed capability condition. + * + * @author Roy Wetherall + */ +public class DestroyedCapabilityCondition extends AbstractCapabilityCondition +{ + @Override + public boolean evaluate(NodeRef nodeRef) + { + return nodeService.hasAspect(nodeRef, ASPECT_GHOSTED); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/FileableCapabilityCondition.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/FileableCapabilityCondition.java new file mode 100644 index 0000000000..942d73c8eb --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/FileableCapabilityCondition.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability.declarative.condition; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.capability.declarative.AbstractCapabilityCondition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * Fileable capability condition. Indicates whether a node is 'fileable', namely if it extends cm:content + * or extends rma:nonElectronicDocument + * + * @author Roy Wetherall + */ +public class FileableCapabilityCondition extends AbstractCapabilityCondition +{ + /** Dictionary service */ + private DictionaryService dictionaryService; + + /** + * @param dictionaryService dictionary service + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.capability.declarative.CapabilityCondition#evaluate(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public boolean evaluate(NodeRef nodeRef) + { + QName type = nodeService.getType(nodeRef); + // TODO and not already a record? + return (dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT) || + dictionaryService.isSubClass(type, TYPE_NON_ELECTRONIC_DOCUMENT)); + } + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/FillingCapabilityCondition.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/FillingCapabilityCondition.java new file mode 100644 index 0000000000..f199155f3e --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/FillingCapabilityCondition.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability.declarative.condition; + +import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; +import org.alfresco.module.org_alfresco_module_rm.capability.declarative.AbstractCapabilityCondition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.AccessStatus; + +/** + * Filling capability condition. + * + * @author Roy Wetherall + */ +public class FillingCapabilityCondition extends AbstractCapabilityCondition +{ + /** + * @see org.alfresco.module.org_alfresco_module_rm.capability.declarative.CapabilityCondition#evaluate(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public boolean evaluate(NodeRef nodeRef) + { + boolean result = false; + + if (permissionService.hasPermission(nodeRef, RMPermissionModel.FILE_RECORDS) != AccessStatus.DENIED) + { + result = true; + } + + return result; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/FrozenCapabilityCondition.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/FrozenCapabilityCondition.java new file mode 100644 index 0000000000..11d74e4d63 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/FrozenCapabilityCondition.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability.declarative.condition; + +import org.alfresco.module.org_alfresco_module_rm.capability.declarative.AbstractCapabilityCondition; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * @author Roy Wetherall + */ +public class FrozenCapabilityCondition extends AbstractCapabilityCondition +{ +private boolean checkChildren = false; + + public void setCheckChildren(boolean checkChildren) + { + this.checkChildren = checkChildren; + } + + @Override + public boolean evaluate(NodeRef nodeRef) + { + boolean result = rmService.isFrozen(nodeRef); + if (result == false && checkChildren == true) + { + result = rmService.hasFrozenChildren(nodeRef); + } + return result; + } + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/FrozenOrHoldCondition.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/FrozenOrHoldCondition.java new file mode 100644 index 0000000000..025f63d9b4 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/FrozenOrHoldCondition.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability.declarative.condition; + +import org.alfresco.module.org_alfresco_module_rm.FilePlanComponentKind; +import org.alfresco.module.org_alfresco_module_rm.capability.declarative.AbstractCapabilityCondition; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Indicates whether the node is either frozen or is a hold object + * + * @author Roy Wetherall + */ +public class FrozenOrHoldCondition extends AbstractCapabilityCondition +{ + /** + * @see org.alfresco.module.org_alfresco_module_rm.capability.declarative.CapabilityCondition#evaluate(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public boolean evaluate(NodeRef nodeRef) + { + FilePlanComponentKind kind = rmService.getFilePlanComponentKind(nodeRef); + return (rmService.isFrozen(nodeRef) || + (kind != null && kind.equals(FilePlanComponentKind.HOLD))); + } + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/HasEventsCapabilityCondition.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/HasEventsCapabilityCondition.java new file mode 100644 index 0000000000..c2270ddced --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/HasEventsCapabilityCondition.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability.declarative.condition; + +import org.alfresco.module.org_alfresco_module_rm.capability.declarative.AbstractCapabilityCondition; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Indicates whether a scheduled record or folder has events or not. + * + * @author Roy Wetherall + */ +public class HasEventsCapabilityCondition extends AbstractCapabilityCondition +{ + /** Disposition service */ + private DispositionService dispositionService; + + /** + * @param dispositionService disposition service + */ + public void setDispositionService(DispositionService dispositionService) + { + this.dispositionService = dispositionService; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.capability.declarative.CapabilityCondition#evaluate(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public boolean evaluate(NodeRef nodeRef) + { + boolean result = false; + + if (dispositionService.isDisposableItem(nodeRef) == true) + { + DispositionAction da = dispositionService.getNextDispositionAction(nodeRef); + if (da != null) + { + result = (da.getEventCompletionDetails().isEmpty() == false); + } + } + + return result; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/IsScheduledCapabilityCondition.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/IsScheduledCapabilityCondition.java new file mode 100644 index 0000000000..1f9af16c9d --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/IsScheduledCapabilityCondition.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability.declarative.condition; + +import org.alfresco.module.org_alfresco_module_rm.capability.declarative.AbstractCapabilityCondition; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Indicates whether the given disposition action 'may' be scheduled in the future + * + * @author Roy Wetherall + */ +public class IsScheduledCapabilityCondition extends AbstractCapabilityCondition +{ + /** Disposition action */ + private String dispositionAction; + + /** Disposition service */ + private DispositionService dispositionService; + + /** + * @param dispositionAction disposition action + */ + public void setDispositionAction(String dispositionAction) + { + this.dispositionAction = dispositionAction; + } + + /** + * @param dispositionService disposition service + */ + public void setDispositionService(DispositionService dispositionService) + { + this.dispositionService = dispositionService; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.capability.declarative.CapabilityCondition#evaluate(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public boolean evaluate(NodeRef nodeRef) + { + boolean result = false; + + DispositionAction nextDispositionAction = dispositionService.getNextDispositionAction(nodeRef); + if (nextDispositionAction != null) + { + // Get the disposition actions name + String actionName = nextDispositionAction.getName(); + if (actionName.equals(dispositionAction) == true && + dispositionService.isNextDispositionActionEligible(nodeRef) == true) + { + result = true; + } + } + + return result; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/MayBeScheduledCapabilityCondition.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/MayBeScheduledCapabilityCondition.java new file mode 100644 index 0000000000..a0f7c09ba2 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/MayBeScheduledCapabilityCondition.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability.declarative.condition; + +import org.alfresco.module.org_alfresco_module_rm.capability.declarative.AbstractCapabilityCondition; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Indicates whether the given disposition action 'may' be scheduled in the future + * + * @author Roy Wetherall + */ +public class MayBeScheduledCapabilityCondition extends AbstractCapabilityCondition +{ + /** Disposition action */ + private String dispositionAction; + + /** Disposition service */ + private DispositionService dispositionService; + + /** + * @param dispositionAction disposition action + */ + public void setDispositionAction(String dispositionAction) + { + this.dispositionAction = dispositionAction; + } + + /** + * @param dispositionService disposition service + */ + public void setDispositionService(DispositionService dispositionService) + { + this.dispositionService = dispositionService; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.capability.declarative.CapabilityCondition#evaluate(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public boolean evaluate(NodeRef nodeRef) + { + boolean result = false; + + DispositionSchedule dispositionSchedule = dispositionService.getDispositionSchedule(nodeRef); + if (dispositionSchedule != null) + { + if (checkDispositionLevel(nodeRef, dispositionSchedule) == true) + { + for (DispositionActionDefinition dispositionActionDefinition : dispositionSchedule.getDispositionActionDefinitions()) + { + if (dispositionActionDefinition.getName().equals(dispositionAction) == true) + { + result = true; + break; + } + } + } + + } + return result; + } + + /** + * Checks the disposition level + * + * @param nodeRef + * @param dispositionSchedule + * @return + */ + private boolean checkDispositionLevel(NodeRef nodeRef, DispositionSchedule dispositionSchedule) + { + boolean result = false; + boolean isRecordLevelDisposition = dispositionSchedule.isRecordLevelDisposition(); + if (rmService.isRecord(nodeRef) == true && isRecordLevelDisposition == true) + { + result = true; + } + else if (rmService.isRecordFolder(nodeRef) == true && isRecordLevelDisposition == false) + + { + result = true; + } + return result; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/TransferredCapabilityCondition.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/TransferredCapabilityCondition.java new file mode 100644 index 0000000000..39cdc3ff24 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/TransferredCapabilityCondition.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability.declarative.condition; + +import org.alfresco.module.org_alfresco_module_rm.capability.declarative.AbstractCapabilityCondition; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * @author Roy Wetherall + */ +public class TransferredCapabilityCondition extends AbstractCapabilityCondition +{ + @Override + public boolean evaluate(NodeRef nodeRef) + { + return nodeService.hasAspect(nodeRef, RecordsManagementModel.ASPECT_TRANSFERRED); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/VitalRecordOrFolderCapabilityCondition.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/VitalRecordOrFolderCapabilityCondition.java new file mode 100644 index 0000000000..3160caf29a --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/VitalRecordOrFolderCapabilityCondition.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability.declarative.condition; + +import org.alfresco.module.org_alfresco_module_rm.capability.declarative.AbstractCapabilityCondition; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * @author Roy Wetherall + */ +public class VitalRecordOrFolderCapabilityCondition extends AbstractCapabilityCondition +{ + @Override + public boolean evaluate(NodeRef nodeRef) + { + boolean result = false; + + if (rmService.isRecord(nodeRef) == true) + { + // Check the record for the vital record aspect + result = nodeService.hasAspect(nodeRef, RecordsManagementModel.ASPECT_VITAL_RECORD); + } + else if (rmService.isRecordFolder(nodeRef) == true) + { + // Check the folder for the vital record indicator + Boolean value = (Boolean)nodeService.getProperty(nodeRef, RecordsManagementModel.PROP_VITAL_RECORD_INDICATOR); + if (value != null) + { + result = value.booleanValue(); + } + } + + return result; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/ChangeOrDeleteReferencesCapability.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/ChangeOrDeleteReferencesCapability.java new file mode 100644 index 0000000000..1d7143be12 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/ChangeOrDeleteReferencesCapability.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability.impl; + +import net.sf.acegisecurity.vote.AccessDecisionVoter; + +import org.alfresco.module.org_alfresco_module_rm.capability.declarative.DeclarativeCapability; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Change or delete references capability + * + * @author Roy Wetherall + */ +public class ChangeOrDeleteReferencesCapability extends DeclarativeCapability +{ + /** + * @see org.alfresco.module.org_alfresco_module_rm.capability.declarative.DeclarativeCapability#evaluateImpl(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected int evaluateImpl(NodeRef nodeRef) + { + // Can't be sure, because we don't have information about the target so we still abstain + return AccessDecisionVoter.ACCESS_ABSTAIN; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.capability.AbstractCapability#evaluate(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef) + */ + public int evaluate(NodeRef source, NodeRef target) + { + if (rmService.isFilePlanComponent(source)) + { + if (target != null) + { + if (rmService.isFilePlanComponent(target) == true) + { + if (checkConditions(source) == true && checkConditions(target) == true) + { + if (checkPermissions(source) == true && checkPermissions(target) == true) + { + return AccessDecisionVoter.ACCESS_GRANTED; + } + } + } + } + else + { + if (checkConditions(source) == true) + { + if (checkPermissions(source) == true) + { + return AccessDecisionVoter.ACCESS_GRANTED; + } + } + } + + return AccessDecisionVoter.ACCESS_DENIED; + } + else + { + return AccessDecisionVoter.ACCESS_ABSTAIN; + } + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/CreateCapability.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/CreateCapability.java new file mode 100644 index 0000000000..a54bda2968 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/CreateCapability.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability.impl; + +import java.util.HashMap; +import java.util.Map; + +import net.sf.acegisecurity.vote.AccessDecisionVoter; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; +import org.alfresco.module.org_alfresco_module_rm.capability.declarative.DeclarativeCapability; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.namespace.QName; + +/** + * Create group capability implementation + * + * @author Andy Hind + */ +public class CreateCapability extends DeclarativeCapability +{ + /** + * @see org.alfresco.module.org_alfresco_module_rm.capability.Capability#evaluate(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public int evaluate(NodeRef nodeRef) + { + return evaluate(nodeRef, null, null, null); + } + + /** + * + * @param destination + * @param linkee + * @param type + * @param assocType + * @return + */ + public int evaluate(NodeRef destination, NodeRef linkee, QName type, QName assocType) + { + if (linkee != null) + { + int state = checkRead(linkee, true); + if (state != AccessDecisionVoter.ACCESS_GRANTED) + { + return AccessDecisionVoter.ACCESS_DENIED; + } + } + if (rmService.isFilePlanComponent(destination)) + { + if ((assocType == null) || assocType.equals(ContentModel.ASSOC_CONTAINS) == false) + { + if(linkee == null) + { + if(rmService.isRecord(destination) && rmService.isRecordDeclared(destination) == false) + { + if (permissionService.hasPermission(destination, RMPermissionModel.FILE_RECORDS) == AccessStatus.ALLOWED) + { + return AccessDecisionVoter.ACCESS_GRANTED; + } + } + } + else + { + if(rmService.isRecord(linkee) && rmService.isRecord(destination) && rmService.isRecordDeclared(destination) == false) + { + if (permissionService.hasPermission(destination, RMPermissionModel.FILE_RECORDS) == AccessStatus.ALLOWED) + { + return AccessDecisionVoter.ACCESS_GRANTED; + } + } + } + + } + + // Build the conditions map + Map conditions = new HashMap(5); + conditions.put("capabilityCondition.filling", Boolean.TRUE); + conditions.put("capabilityCondition.frozen", Boolean.FALSE); + conditions.put("capabilityCondition.closed", Boolean.FALSE); + conditions.put("capabilityCondition.cutoff", Boolean.FALSE); + + if (checkConditions(destination, conditions) == true) + { + if (rmService.isRecordFolder(destination)) + { + if (permissionService.hasPermission(destination, RMPermissionModel.FILE_RECORDS) == AccessStatus.ALLOWED) + { + return AccessDecisionVoter.ACCESS_GRANTED; + } + } + } + + conditions.put("capabilityCondition.closed", Boolean.TRUE); + if (checkConditions(destination, conditions) == true) + { + if (rmService.isRecordFolder(destination)) + { + if (permissionService.hasPermission(rmService.getFilePlan(destination), RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS) == AccessStatus.ALLOWED) + { + return AccessDecisionVoter.ACCESS_GRANTED; + } + } + } + + conditions.remove("capabilityCondition.closed"); + conditions.put("capabilityCondition.cutoff", Boolean.TRUE); + if (checkConditions(destination, conditions) == true) + { + if (rmService.isRecordFolder(destination)) + { + if (permissionService.hasPermission(rmService.getFilePlan(destination), RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS) == AccessStatus.ALLOWED) + { + return AccessDecisionVoter.ACCESS_GRANTED; + } + } + } + } + if (capabilityService.getCapability(RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS).evaluate(destination) == AccessDecisionVoter.ACCESS_GRANTED) + { + return AccessDecisionVoter.ACCESS_GRANTED; + } + if (capabilityService.getCapability(RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS).evaluate(destination) == AccessDecisionVoter.ACCESS_GRANTED) + { + return AccessDecisionVoter.ACCESS_GRANTED; + } + if (capabilityService.getCapability(RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS).evaluate(destination) == AccessDecisionVoter.ACCESS_GRANTED) + { + return AccessDecisionVoter.ACCESS_GRANTED; + } + if (capabilityService.getCapability(RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA).evaluate(destination) == AccessDecisionVoter.ACCESS_GRANTED) + { + return AccessDecisionVoter.ACCESS_GRANTED; + } + if (((ChangeOrDeleteReferencesCapability)capabilityService.getCapability(RMPermissionModel.CHANGE_OR_DELETE_REFERENCES)).evaluate(destination, linkee) == AccessDecisionVoter.ACCESS_GRANTED) + { + return AccessDecisionVoter.ACCESS_GRANTED; + } + return AccessDecisionVoter.ACCESS_DENIED; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/DeleteLinksCapability.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/DeleteLinksCapability.java new file mode 100644 index 0000000000..75d7347ae2 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/DeleteLinksCapability.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability.impl; + +import net.sf.acegisecurity.vote.AccessDecisionVoter; + +import org.alfresco.module.org_alfresco_module_rm.capability.declarative.DeclarativeCapability; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Delete links capability. + * + * @author Roy Wetherall + */ +public class DeleteLinksCapability extends DeclarativeCapability +{ + /** + * @see org.alfresco.module.org_alfresco_module_rm.capability.Capability#evaluate(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public int evaluate(NodeRef nodeRef) + { + // no way to know ... + return AccessDecisionVoter.ACCESS_ABSTAIN; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.capability.AbstractCapability#evaluate(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef) + */ + public int evaluate(NodeRef source, NodeRef target) + { + if (rmService.isFilePlan(source) == true && rmService.isFilePlan(target) == true) + { + if (checkConditions(source) == true && checkConditions(target) == true) + { + if (checkPermissions(source) == true && checkPermissions(target) == true) + { + return AccessDecisionVoter.ACCESS_GRANTED; + } + } + return AccessDecisionVoter.ACCESS_DENIED; + } + else + { + return AccessDecisionVoter.ACCESS_ABSTAIN; + } + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/EditRecordMetadataCapability.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/EditRecordMetadataCapability.java new file mode 100644 index 0000000000..e93f534173 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/EditRecordMetadataCapability.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability.impl; + +import net.sf.acegisecurity.vote.AccessDecisionVoter; + +import org.alfresco.module.org_alfresco_module_rm.capability.declarative.DeclarativeCapability; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.OwnableService; + +/** + * Edit record metadata capability implementation. + * + * @author Roy Wetherall + */ +public class EditRecordMetadataCapability extends DeclarativeCapability +{ + private OwnableService ownableService; + + private OwnableService getOwnableService() + { + if (ownableService == null) + { + ownableService = (OwnableService)applicationContext.getBean("OwnableService"); + } + return ownableService; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.capability.declarative.DeclarativeCapability#evaluate(org.alfresco.service.cmr.repository.NodeRef) + */ + public int evaluate(final NodeRef nodeRef) + { + // The default state is not knowing anything + int result = AccessDecisionVoter.ACCESS_ABSTAIN; + + // Check we are dealing with a file plan component + if (rmService.isFilePlanComponent(nodeRef) == true) + { + // Now the default state is denied + result = AccessDecisionVoter.ACCESS_DENIED; + + // Check the kind of the object, the permissions and the conditions + if (checkKinds(nodeRef) == true && checkConditions(nodeRef) == true) + { + if (checkPermissions(nodeRef) == true) + { + result = AccessDecisionVoter.ACCESS_GRANTED; + } + else + { + result = AuthenticationUtil.runAs(new RunAsWork() + { + @Override + public Integer doWork() throws Exception + { + Integer result = Integer.valueOf(AccessDecisionVoter.ACCESS_DENIED); + + // Since we know this is undeclared if you are the owner then you should be able to + // edit the records meta-data (otherwise how can it be declared by the user?) + if (getOwnableService().hasOwner(nodeRef) == true) + { + String user = AuthenticationUtil.getFullyAuthenticatedUser(); + if (user != null && + getOwnableService().getOwner(nodeRef).equals(user) == true) + { + result = Integer.valueOf(AccessDecisionVoter.ACCESS_GRANTED); + } + } + + return result; + } + + }, AuthenticationUtil.getSystemUserName()).intValue(); + } + } + } + + return result; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/MoveRecordsCapability.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/MoveRecordsCapability.java new file mode 100644 index 0000000000..4b25b7f271 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/MoveRecordsCapability.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability.impl; + +import net.sf.acegisecurity.vote.AccessDecisionVoter; + +import org.alfresco.module.org_alfresco_module_rm.capability.declarative.DeclarativeCapability; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.namespace.QName; + +public class MoveRecordsCapability extends DeclarativeCapability +{ + @Override + public int evaluate(NodeRef nodeRef) + { + // no way to know ... + return AccessDecisionVoter.ACCESS_ABSTAIN; + } + + public int evaluate(NodeRef movee, NodeRef destination) + { + int state = AccessDecisionVoter.ACCESS_ABSTAIN; + + if (rmService.isFilePlanComponent(destination)) + { + state = checkRead(movee, true); + if (state != AccessDecisionVoter.ACCESS_GRANTED) + { + return AccessDecisionVoter.ACCESS_DENIED; + } + + if (rmService.isFilePlanComponent(movee) == true) + { + state = capabilityService.getCapability("Delete").evaluate(movee); + } + else + { + if (checkPermissionsImpl(movee, PermissionService.DELETE) == true) + { + state = AccessDecisionVoter.ACCESS_GRANTED; + } + } + + if (state == AccessDecisionVoter.ACCESS_GRANTED) + { + QName type = nodeService.getType(movee); + // now we know the node - we can abstain for certain types and aspects (eg, rm) + CreateCapability createCapability = (CreateCapability)capabilityService.getCapability("Create"); + state = createCapability.evaluate(destination, movee, type, null); + + if (state == AccessDecisionVoter.ACCESS_GRANTED) + { + if (rmService.isFilePlanComponent(movee) == true) + { + if (checkPermissionsImpl(movee, MOVE_RECORDS) == true) + { + return AccessDecisionVoter.ACCESS_GRANTED; + } + } + else + { + return AccessDecisionVoter.ACCESS_GRANTED; + } + } + } + + return AccessDecisionVoter.ACCESS_DENIED; + } + else + { + return AccessDecisionVoter.ACCESS_ABSTAIN; + } + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/UpdateCapability.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/UpdateCapability.java new file mode 100644 index 0000000000..db528dcd0c --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/UpdateCapability.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability.impl; + +import java.io.Serializable; +import java.util.Map; + +import net.sf.acegisecurity.vote.AccessDecisionVoter; + +import org.alfresco.module.org_alfresco_module_rm.capability.declarative.CompositeCapability; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * Update capability implementation. + * + * @author andyh + */ +public class UpdateCapability extends CompositeCapability +{ + /** + * + * @param nodeRef + * @param aspectQName + * @param properties + * @return + */ + public int evaluate(NodeRef nodeRef, QName aspectQName, Map properties) + { + if ((aspectQName != null) && (voter.isProtectedAspect(nodeRef, aspectQName))) + { + return AccessDecisionVoter.ACCESS_DENIED; + } + + if ((properties != null) && (voter.includesProtectedPropertyChange(nodeRef, properties))) + { + return AccessDecisionVoter.ACCESS_DENIED; + } + + return evaluate(nodeRef); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/UpdatePropertiesCapability.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/UpdatePropertiesCapability.java new file mode 100644 index 0000000000..130b12849a --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/UpdatePropertiesCapability.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability.impl; + +import java.io.Serializable; +import java.util.Map; + +import net.sf.acegisecurity.vote.AccessDecisionVoter; + +import org.alfresco.module.org_alfresco_module_rm.capability.declarative.CompositeCapability; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * Update properties capability + * + * @author andyh + */ +public class UpdatePropertiesCapability extends CompositeCapability +{ + /** + * Evaluate capability, taking into account the protected properties. + * + * @param nodeRef node reference + * @param properties updated properties, if no null + */ + public int evaluate(NodeRef nodeRef, Map properties) + { + if ((properties != null) && (voter.includesProtectedPropertyChange(nodeRef, properties))) + { + return AccessDecisionVoter.ACCESS_DENIED; + } + + return evaluate(nodeRef); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/ViewRecordsCapability.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/ViewRecordsCapability.java new file mode 100644 index 0000000000..e00aba7985 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/ViewRecordsCapability.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.capability.impl; + +import net.sf.acegisecurity.vote.AccessDecisionVoter; + +import org.alfresco.module.org_alfresco_module_rm.capability.declarative.DeclarativeCapability; +import org.alfresco.service.cmr.repository.NodeRef; + +public final class ViewRecordsCapability extends DeclarativeCapability +{ + /** + * @see org.alfresco.module.org_alfresco_module_rm.capability.declarative.DeclarativeCapability#evaluate(org.alfresco.service.cmr.repository.NodeRef) + */ + public int evaluate(NodeRef nodeRef) + { + if (nodeRef != null) + { + if (rmService.isFilePlanComponent(nodeRef) == true) + { + return checkRmRead(nodeRef); + } + } + + return AccessDecisionVoter.ACCESS_ABSTAIN; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/PivotUtil.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/PivotUtil.java new file mode 100644 index 0000000000..efd01a3935 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/PivotUtil.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.caveat; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/* package scope */ class PivotUtil +{ + static Map> getPivot(Map> source) + { + + Map> pivot = new HashMap>(); + + for(String authority : source.keySet()) + { + Listvalues = source.get(authority); + for(String value : values) + { + if(pivot.containsKey(value)) + { + // already exists + List list = pivot.get(value); + list.add(authority); + } + else + { + // New value + List list = new ArrayList(); + list.add(authority); + pivot.put(value, list); + } + } + } + return pivot; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/RMCaveatConfigComponent.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/RMCaveatConfigComponent.java new file mode 100644 index 0000000000..610e172b39 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/RMCaveatConfigComponent.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.caveat; + +import java.io.File; +import java.io.InputStream; +import java.util.List; +import java.util.Map; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +public interface RMCaveatConfigComponent +{ + public void init(); + + /** + * Get allowed values for given caveat list (for current user) + * + * @param constraintName + * @return + */ + public List getRMAllowedValues(String constraintName); + + /** + * Get custom caveat models + * + * @return + */ + public List getRMCaveatModels(); + + /** + * Check whether access to 'record component' node is vetoed for current user due to caveat(s) + * + * @param nodeRef + * @return false, if caveat(s) veto access otherwise return true + */ + public boolean hasAccess(NodeRef nodeRef); + + /** + * Get RM constraint list + * + * @param listName the name of the RMConstraintList + */ + public RMConstraintInfo getRMConstraint(String listName); + + /** + * Add RM constraint + */ + public void addRMConstraint(String listName); + + /** + * Add RM constraint value for given authority + */ + public void addRMConstraintListValue(String listName, String authorityName, String value); + + /** + * Update RM constraint values for given authority + */ + public void updateRMConstraintListAuthority(String listName, String authorityName, Listvalues); + + /** + * Update RM constraint authorities for given value + */ + public void updateRMConstraintListValue(String listName, String valueName, Listauthorities); + + /** + * Remove RM constraint value (all authorities) + */ + public void removeRMConstraintListValue(String listName, String valueName); + + /** + * Remove RM constraint authority (all values) + */ + public void removeRMConstraintListAuthority(String listName, String authorityName); + + /** + * Delete RM Constraint + * + * @param listName the name of the RMConstraintList + */ + public void deleteRMConstraint(String listName); + + /** + * Get the details of a caveat list + * @param listName + * @return + */ + public Map> getListDetails(String listName); + + public NodeRef updateOrCreateCaveatConfig(File jsonFile); + + public NodeRef updateOrCreateCaveatConfig(String jsonString); + + public NodeRef updateOrCreateCaveatConfig(InputStream is); +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/RMCaveatConfigComponentImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/RMCaveatConfigComponentImpl.java new file mode 100644 index 0000000000..09a94de97a --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/RMCaveatConfigComponentImpl.java @@ -0,0 +1,945 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.caveat; + +import java.io.File; +import java.io.InputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.caveat.RMListOfValuesConstraint.MatchLogic; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.content.ContentServicePolicies; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.version.VersionModel; +import org.alfresco.service.cmr.dictionary.Constraint; +import org.alfresco.service.cmr.dictionary.ConstraintDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.JSONtoFmModel; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * RM Caveat Config component impl + * + * @author janv + */ +public class RMCaveatConfigComponentImpl implements ContentServicePolicies.OnContentUpdatePolicy, + NodeServicePolicies.BeforeDeleteNodePolicy, + NodeServicePolicies.OnCreateNodePolicy, + RMCaveatConfigComponent +{ + private static Log logger = LogFactory.getLog(RMCaveatConfigComponentImpl.class); + + private PolicyComponent policyComponent; + private ContentService contentService; + private DictionaryService dictionaryService; + private NamespaceService namespaceService; + private AuthorityService authorityService; + private PersonService personService; + private NodeService nodeService; + + // Default + private StoreRef storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore"); + + private List caveatAspectURINames = new ArrayList(0); + private List caveatAspectQNames = new ArrayList(0); + + private List caveatModelURINames = new ArrayList(0); + private List caveatModelQNames = new ArrayList(0); + + private static final String CAVEAT_CONFIG_NAME = "caveatConfig.json"; + + private static final QName DATATYPE_TEXT = DataTypeDefinition.TEXT; + + + /* + * Caveat Config + * first string is property name + * second string is authority name (user or group full name) + * third string is list of values of property + */ + + // TODO - convert to SimpleCache to be cluster-aware (for dynamic changes to caveat config across a cluster) + private Map>> caveatConfig = new ConcurrentHashMap>>(2); + + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + public void setAuthorityService(AuthorityService authorityService) + { + this.authorityService = authorityService; + } + + public void setPersonService(PersonService personService) + { + this.personService = personService; + } + + public void setStoreRef(String storeRef) + { + this.storeRef = new StoreRef(storeRef); + } + + public void setCaveatAspects(List caveatAspectNames) + { + this.caveatAspectURINames = caveatAspectNames; + } + + public void setCaveatModels(List caveatModelNames) + { + this.caveatModelURINames = caveatModelNames; + } + + /** + * Initialise behaviours and caveat config cache + */ + public void init() + { + // Register interest in the onContentUpdate policy + policyComponent.bindClassBehaviour( + ContentServicePolicies.OnContentUpdatePolicy.QNAME, + RecordsManagementModel.TYPE_CAVEAT_CONFIG, + new JavaBehaviour(this, "onContentUpdate")); + + // Register interest in the beforeDeleteNode policy + policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"), + RecordsManagementModel.TYPE_CAVEAT_CONFIG, + new JavaBehaviour(this, "beforeDeleteNode")); + + // Register interest in the onCreateNode policy + policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateNode"), + RecordsManagementModel.TYPE_CAVEAT_CONFIG, + new JavaBehaviour(this, "onCreateNode")); + + if (caveatAspectURINames.size() > 0) + { + for (String caveatAspectURIName : caveatAspectURINames) + { + caveatAspectQNames.add(QName.createQName(caveatAspectURIName)); + } + + if (logger.isInfoEnabled()) + { + logger.info("Caveat aspects configured "+caveatAspectQNames); + } + } + else + { + logger.warn("No caveat aspects configured - caveats will not be applied"); + } + + if (caveatModelURINames.size() > 0) + { + for (String caveatModelURIName : caveatModelURINames) + { + caveatModelQNames.add(QName.createQName(caveatModelURIName)); + } + + if (logger.isInfoEnabled()) + { + logger.info("Caveat models configured "+caveatModelQNames); + } + } + else + { + logger.info("No caveat models configured - all models will be checked"); + } + + NodeRef caveatConfigNodeRef = getCaveatConfigNode(); + if (caveatConfigNodeRef != null) + { + validateAndReset(caveatConfigNodeRef); + } + } + + public void onContentUpdate(NodeRef nodeRef, boolean newContent) + { + if (logger.isInfoEnabled()) + { + logger.info("onContentUpdate: "+nodeRef+", "+newContent); + } + + validateAndReset(nodeRef); + } + + public void beforeDeleteNode(NodeRef nodeRef) + { + if (logger.isInfoEnabled()) + { + logger.info("beforeDeleteNode: "+nodeRef); + } + + validateAndReset(nodeRef); + } + + public void onCreateNode(ChildAssociationRef childAssocRef) + { + if (logger.isInfoEnabled()) + { + logger.info("onCreateNode: "+childAssocRef); + } + + validateAndReset(childAssocRef.getChildRef()); + } + + @SuppressWarnings("unchecked") + protected void validateAndReset(NodeRef nodeRef) + { + ContentReader cr = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT); + if (cr != null) + { + // TODO - check who can change caveat config ! + // TODO - locking (or checkout/checkin) + + String caveatConfigData = cr.getContentString(); + if (caveatConfigData != null) + { + NodeRef existing = getCaveatConfigNode(); + if ((existing != null && (! existing.equals(nodeRef)))) + { + throw new AlfrescoRuntimeException("Cannot create more than one caveat config (existing="+existing+", new="+nodeRef+")"); + } + + try + { + if (logger.isTraceEnabled()) + { + logger.trace(caveatConfigData); + } + + Set models = new HashSet(1); + Set props = new HashSet(10); + Set expectedPrefixes = new HashSet(10); + + if (caveatModelQNames.size() > 0) + { + models.addAll(caveatModelQNames); + } + else + { + models.addAll(dictionaryService.getAllModels()); + } + + if (logger.isTraceEnabled()) + { + logger.trace("validateAndReset: models to check "+models); + } + + for (QName model : models) + { + props.addAll(dictionaryService.getProperties(model, DATATYPE_TEXT)); + expectedPrefixes.addAll(namespaceService.getPrefixes(model.getNamespaceURI())); + } + + if (props.size() == 0) + { + logger.warn("validateAndReset: no caveat properties found"); + } + else + { + if (logger.isTraceEnabled()) + { + logger.trace("validateAndReset: properties to check "+props); + } + } + + Map caveatConfigMap = JSONtoFmModel.convertJSONObjectToMap(caveatConfigData); + + for (Map.Entry conEntry : caveatConfigMap.entrySet()) + { + String conStr = conEntry.getKey(); + + QName conQName = QName.resolveToQName(namespaceService, conStr); + + // check prefix + String conPrefix = QName.splitPrefixedQName(conStr)[0]; + boolean prefixFound = false; + for (String expectedPrefix : expectedPrefixes) + { + if (conPrefix.equals(expectedPrefix)) + { + prefixFound = true; + } + } + + if (! prefixFound) + { + throw new AlfrescoRuntimeException("Unexpected prefix: "+ conPrefix + " (" + conStr +") expected one of "+expectedPrefixes+")"); + } + + Map> caveatMap = (Map>)conEntry.getValue(); + + List allowedValues = null; + boolean found = false; + + for (QName propertyName : props) + { + PropertyDefinition propDef = dictionaryService.getProperty(propertyName); + List conDefs = propDef.getConstraints(); + for (ConstraintDefinition conDef : conDefs) + { + final Constraint con = conDef.getConstraint(); + if (con instanceof RMListOfValuesConstraint) + { + String conName = ((RMListOfValuesConstraint)con).getShortName(); + if (conName.equals(conStr)) + { + // note: assumes only one caveat/LOV against a given property + allowedValues = AuthenticationUtil.runAs(new RunAsWork>() + { + public List doWork() + { + return ((RMListOfValuesConstraint)con).getAllowedValues(); + } + }, AuthenticationUtil.getSystemUserName()); + + found = true; + break; + } + } + } + } + + if (! found) + { + //throw new AlfrescoRuntimeException("Constraint does not exist (or is not used): "+conStr); + } + + if (allowedValues != null) + { + if (logger.isInfoEnabled()) + { + logger.info("Processing constraint: "+conQName); + } + + for (Map.Entry> caveatEntry : caveatMap.entrySet()) + { + String authorityName = caveatEntry.getKey(); + List caveatList = caveatEntry.getValue(); + + // validate authority (user or group) - note: groups are configured with fullname (ie. GROUP_xxx) + if ((! authorityService.authorityExists(authorityName) && ! personService.personExists(authorityName))) + { + // TODO - review warnings (& I18N) + String msg = "User/group does not exist: "+authorityName+" (constraint="+conStr+")"; + logger.warn(msg); + } + + // validate caveat list + for (String value : caveatList) + { + if (! allowedValues.contains(value)) + { + // TODO - review warnings (& add I18N) + String msg = "Invalid value in list: "+value+" (authority="+authorityName+", constraint="+conStr+")"; + logger.warn(msg); + } + } + } + } + } + + // Valid, so update + caveatConfig.clear(); + + for (Map.Entry conEntry : caveatConfigMap.entrySet()) + { + String conStr = conEntry.getKey(); + Map> caveatMap = (Map>)conEntry.getValue(); + + caveatConfig.put(conStr, caveatMap); + } + } + catch (JSONException e) + { + throw new AlfrescoRuntimeException("Invalid caveat config syntax: "+e); + } + } + } + } + + private NodeRef getCaveatConfigNode() + { + NodeRef rootNode = nodeService.getRootNode(storeRef); + return nodeService.getChildByName(rootNode, RecordsManagementModel.ASSOC_CAVEAT_CONFIG, CAVEAT_CONFIG_NAME); + } + + + public NodeRef updateOrCreateCaveatConfig(InputStream is) + { + NodeRef caveatConfig = getOrCreateCaveatConfig(); + + // Update the content + ContentWriter writer = this.contentService.getWriter(caveatConfig, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent(is); + + return caveatConfig; + } + + public NodeRef updateOrCreateCaveatConfig(File jsonFile) + { + NodeRef caveatConfig = getOrCreateCaveatConfig(); + + // Update the content + ContentWriter writer = this.contentService.getWriter(caveatConfig, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent(jsonFile); + + return caveatConfig; + } + + public NodeRef updateOrCreateCaveatConfig(String jsonString) + { + NodeRef caveatConfig = getOrCreateCaveatConfig(); + + // Update the content + ContentWriter writer = this.contentService.getWriter(caveatConfig, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent(jsonString); + + return caveatConfig; + } + + private NodeRef getOrCreateCaveatConfig() + { + NodeRef caveatConfig = getCaveatConfigNode(); + if (caveatConfig == null) + { + NodeRef rootNode = nodeService.getRootNode(storeRef); + nodeService.addAspect(rootNode, VersionModel.ASPECT_VERSION_STORE_ROOT, null); + + // Create caveat config + caveatConfig = nodeService.createNode(rootNode, + RecordsManagementModel.ASSOC_CAVEAT_CONFIG, + QName.createQName(RecordsManagementModel.RM_URI, CAVEAT_CONFIG_NAME), + RecordsManagementModel.TYPE_CAVEAT_CONFIG).getChildRef(); + + nodeService.setProperty(caveatConfig, ContentModel.PROP_NAME, CAVEAT_CONFIG_NAME); + } + + return caveatConfig; + } + + // Get list of all caveat qualified names + public Set getRMConstraintNames() + { + return caveatConfig.keySet(); + } + + // Get allowed values for given caveat (for current user) + public List getRMAllowedValues(String constraintName) + { + List allowedValues = new ArrayList(0); + + String userName = AuthenticationUtil.getRunAsUser(); + if (userName != null) + { + if (! (AuthenticationUtil.isMtEnabled() && AuthenticationUtil.isRunAsUserTheSystemUser())) + { + // note: userName and userGroupNames must not be null + Map> caveatConstraintDef = caveatConfig.get(constraintName); + Set userGroupFullNames = authorityService.getAuthoritiesForUser(userName); + allowedValues = getRMAllowedValues(userName, userGroupFullNames, constraintName); + } + } + + return allowedValues; + } + + private List getRMAllowedValues(String userName, Set userGroupFullNames, String constraintName) + { + SetallowedValues = new HashSet(); + + // note: userName and userGroupNames must not be null + Map> caveatConstraintDef = caveatConfig.get(constraintName); + + if (caveatConstraintDef != null) + { + List direct = caveatConstraintDef.get(userName); + if(direct != null) + { + allowedValues.addAll(direct); + } + + for (String group : userGroupFullNames) + { + List values = caveatConstraintDef.get(group); + if(values != null) + { + allowedValues.addAll(values); + } + } + } + + Listret = new ArrayList(); + ret.addAll(allowedValues); + return ret; + } + + /** + * Check whether access to 'record component' node is vetoed for current user due to caveat(s) + * + * @param nodeRef + * @return false, if caveat(s) veto access otherwise return true + */ + @SuppressWarnings("unchecked") + public boolean hasAccess(NodeRef nodeRef) + { + if ((! nodeService.exists(nodeRef)) || (caveatAspectQNames.size() == 0)) + { + return true; + } + + boolean found = false; + for (QName caveatAspectQName : caveatAspectQNames) + { + if (nodeService.hasAspect(nodeRef, caveatAspectQName)) + { + found = true; + break; + } + } + + if (! found) + { + // no caveat aspect + return true; + } + else + { + // check for caveats + String userName = AuthenticationUtil.getRunAsUser(); + if (userName != null) + { + // check all text properties + Map props = nodeService.getProperties(nodeRef); + for (Map.Entry entry : props.entrySet()) + { + QName propName = entry.getKey(); + PropertyDefinition propDef = dictionaryService.getProperty(propName); + + if ((propDef != null) && (propDef.getDataType().getName().equals(DATATYPE_TEXT))) + { + List conDefs = propDef.getConstraints(); + for (ConstraintDefinition conDef : conDefs) + { + Constraint con = conDef.getConstraint(); + if (con instanceof RMListOfValuesConstraint) + { + RMListOfValuesConstraint rmCon = ((RMListOfValuesConstraint)con); + String conName = rmCon.getShortName(); + MatchLogic matchLogic = rmCon.getMatchLogicEnum(); + Map> caveatConstraintDef = caveatConfig.get(conName); + if (caveatConstraintDef == null) + { + continue; + } + else + { + Set userGroupNames = authorityService.getAuthoritiesForUser(userName); + List allowedValues = getRMAllowedValues(userName, userGroupNames, conName); + + List propValues = null; + Object val = entry.getValue(); + if (val instanceof String) + { + propValues = new ArrayList(1); + propValues.add((String)val); + } + else if (val instanceof List) + { + propValues = (List)val; + } + + if (propValues != null && !isAllowed(propValues, allowedValues, matchLogic)) + { + if (logger.isDebugEnabled()) + { + logger.debug("Veto access: caveat="+conName+", userName="+userName+", nodeRef="+nodeRef+", propName="+propName+", propValues="+propValues+", allowedValues="+allowedValues); + } + return false; + } + } + } + } + } + } + } + + return true; + } + } + + private boolean isAllowed(List propValues, List userGroupValues, MatchLogic matchLogic) + { + if (matchLogic.equals(MatchLogic.AND)) + { + // check user/group values match all values on node + for (String propValue : propValues) + { + if (! userGroupValues.contains(propValue)) + { + if (logger.isTraceEnabled()) + { + logger.trace("Not allowed: "+propValues+", "+userGroupValues+", "+matchLogic); + } + + return false; + } + } + + return true; + } + else if (matchLogic.equals(MatchLogic.OR)) + { + // check user/group values match at least one value on node + for (String propValue : propValues) + { + if (userGroupValues.contains(propValue)) + { + return true; + } + } + + if (logger.isTraceEnabled()) + { + logger.trace("Not allowed: "+propValues+", "+userGroupValues+", "+matchLogic); + } + + return false; + } + + logger.error("Unexpected match logic type: "+matchLogic); + return false; + } + + /** + * Add a single value to an authority in a list. The existing values of the list remain. + * + * @param listName the name of the RMConstraintList + * @param authorityName + * @param values + * @throws AlfrescoRuntimeException if either the list or the authority do not already exist. + */ + public void addRMConstraintListValue(String listName, String authorityName, String value) + { + Map> members = caveatConfig.get(listName); + if(members == null) + { + throw new AlfrescoRuntimeException("unable to add to list, list not defined:"+ listName); + } + List values = members.get(authorityName); + if(values == null) + { + throw new AlfrescoRuntimeException("Unable to add to authority in list. Authority not member listName: "+ listName + " authorityName:" +authorityName); + } + values.add(value); + updateOrCreateCaveatConfig(convertToJSONString(caveatConfig)); + } + + /** + * Get the member details of the specified list + * @param listName + * @return the details of the specified list + */ + public Map> getListDetails(String listName) + { + return caveatConfig.get(listName); + } + + public List getRMCaveatModels() + { + return caveatModelQNames; + } + + /** + * Replace the values for an authority in a list. + * The existing values are removed. + * + * If the authority does not already exist in the list, it will be added + * + * @param listName the name of the RMConstraintList + * @param authorityName + * @param values + */ + public void updateRMConstraintListAuthority(String listName, String authorityName, Listvalues) + { + Map> members = caveatConfig.get(listName); + if(members == null) + { + // Create the new list, with the authority name + Map> constraint = new HashMap>(0); + constraint.put(authorityName, values); + caveatConfig.put(listName, constraint); + } + else + { + members.put(authorityName, values); + } + + updateOrCreateCaveatConfig(convertToJSONString(caveatConfig)); + } + + /** + * Replace the authorities for a value in a list + * + * @param listName + * @param valueName + * @param authorities + */ + public void updateRMConstraintListValue(String listName, String valueName, Listauthorities) + { + + // members contains member, values[] + Map> members = caveatConfig.get(listName); + + if(members == null) + { + // Members List does not exist + Map> emptyConstraint = new HashMap>(0); + caveatConfig.put(listName, emptyConstraint); + members = emptyConstraint; + + } + // authorities contains authority, values[] + // pivot contains value, members[] + Map> pivot = PivotUtil.getPivot(members); + + // remove all authorities which have this value + List existingAuthorities = pivot.get(valueName); + if(existingAuthorities != null) + { + for(String authority : existingAuthorities) + { + List vals = members.get(authority); + vals.remove(valueName); + } + } + // add the new authorities for this value + for(String authority : authorities) + { + List vals = members.get(authority); + if(vals == null) + { + vals= new ArrayList(); + members.put(authority, vals); + } + vals.add(valueName); + } + + updateOrCreateCaveatConfig(convertToJSONString(caveatConfig)); + } + + public void removeRMConstraintListValue(String listName, String valueName) + { + // members contains member, values[] + Map> members = caveatConfig.get(listName); + + if(members == null) + { + // list does not exist + } + else + { + // authorities contains authority, values[] + // pivot contains value, members[] + Map> pivot = PivotUtil.getPivot(members); + + // remove all authorities which have this value + List existingAuthorities = pivot.get(valueName); + if(existingAuthorities != null) + { + for(String authority : existingAuthorities) + { + List vals = members.get(authority); + vals.remove(valueName); + } + } + + updateOrCreateCaveatConfig(convertToJSONString(caveatConfig)); + } + } + + /** + * Remove an authority from a list + * + * @param listName the name of the RMConstraintList + * @param authorityName + * @param values + */ + public void removeRMConstraintListAuthority(String listName, String authorityName) + { + Map> members = caveatConfig.get(listName); + if(members != null) + { + members.remove(listName); + } + + updateOrCreateCaveatConfig(convertToJSONString(caveatConfig)); + } + + /** + * @param config the configuration to convert + * @return a String containing the JSON representation of the configuration. + */ + private String convertToJSONString(Map>> config) + { + JSONObject obj = new JSONObject(); + + try + { + Set listNames = config.keySet(); + for(String listName : listNames) + { + Map> members = config.get(listName); + + Set authorityNames = members.keySet(); + JSONObject listMembers = new JSONObject(); + + for(String authorityName : authorityNames) + { + listMembers.put(authorityName, members.get(authorityName)); + } + + obj.put(listName, listMembers); + } + } + catch (JSONException je) + { + throw new AlfrescoRuntimeException("Invalid caveat config syntax: "+ je); + } + return obj.toString(); + } + + /** + * Get an RMConstraintInfo + * @param listQName + * @return the constraint or null if it does not exist + */ + public RMConstraintInfo getRMConstraint(QName listQName) + { + ConstraintDefinition dictionaryDef = dictionaryService.getConstraint(listQName); + if(dictionaryDef != null) + { + Constraint con = dictionaryDef.getConstraint(); + if (con instanceof RMListOfValuesConstraint) + { + final RMListOfValuesConstraint def = (RMListOfValuesConstraint)con; + + RMConstraintInfo info = new RMConstraintInfo(); + info.setName(listQName.toPrefixString()); + info.setTitle(con.getTitle()); + List allowedValues = AuthenticationUtil.runAs(new RunAsWork>() + { + public List doWork() + { + return def.getAllowedValues(); + } + }, AuthenticationUtil.getSystemUserName()); + + info.setAllowedValues(allowedValues.toArray(new String[allowedValues.size()])); + info.setCaseSensitive(def.isCaseSensitive()); + return info; + } + } + return null; + } + + /** + * Get RM Constraint detail. + * + * @return the constraintInfo or null + */ + public RMConstraintInfo getRMConstraint(String listName) + { + QName listQName = QName.createQName(listName, namespaceService); + return getRMConstraint(listQName); + } + + public void deleteRMConstraint(String listName) + { + caveatConfig.remove(listName); + updateOrCreateCaveatConfig(convertToJSONString(caveatConfig)); + } + + public void addRMConstraint(String listName) + { + Map> emptyConstraint = new HashMap>(0); + caveatConfig.put(listName, emptyConstraint); + updateOrCreateCaveatConfig(convertToJSONString(caveatConfig)); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/RMCaveatConfigService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/RMCaveatConfigService.java new file mode 100644 index 0000000000..73e33738f5 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/RMCaveatConfigService.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.caveat; + +import java.io.File; +import java.io.InputStream; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.service.cmr.repository.NodeRef; + +public interface RMCaveatConfigService +{ + public void init(); + + /** + * Get allowed values for given caveat list (for current user) + * @param constraintName + * @return + */ + public List getRMAllowedValues(String constraintName); + + /** + * Check whether access to 'record component' node is vetoed for current user due to caveat(s) + * + * @param nodeRef + * @return false, if caveat(s) veto access otherwise return true + */ + public boolean hasAccess(NodeRef nodeRef); + + /* + * Get a single RM constraint + */ + public RMConstraintInfo getRMConstraint(String listName); + + /* + * Get the names of all the caveat lists + */ + public Set getAllRMConstraints(); + + /** + * Get the details of a caveat list + * @param listName + * @return + */ + public Map> getListDetails(String listName); + + public NodeRef updateOrCreateCaveatConfig(File jsonFile); + + public NodeRef updateOrCreateCaveatConfig(String jsonString); + + public NodeRef updateOrCreateCaveatConfig(InputStream is); + + /** + * add RM constraint list + * @param listName the name of the RMConstraintList + * @param listTitle + */ + public RMConstraintInfo addRMConstraint(String listName, String listTitle, String[] allowedValues); + + /** + * update RM constraint list allowed values + * @param listName the name of the RMConstraintList - can not be changed + * @param allowedValues + */ + public RMConstraintInfo updateRMConstraintAllowedValues(String listName, String[] allowedValues); + + /** + * update RM constraint Title + * @param listName the name of the RMConstraintList - can not be changed + * @param allowedValues + */ + public RMConstraintInfo updateRMConstraintTitle(String listName, String newTitle); + + + /** + * delete RM Constraint + * + * @param listName the name of the RMConstraintList + */ + public void deleteRMConstraint(String listName); + + /** + * Add a single value to an authority in a list. The existing values of the list remain. + * + * @param listName the name of the RMConstraintList + * @param authorityName + * @param values + * @throws AlfrescoRuntimeException if either the list or the authority do not already exist. + */ + public void addRMConstraintListValue(String listName, String authorityName, String value); + + /** + * Replace the values for an authority in a list. + * The existing values are removed. + * + * If the authority does not already exist in the list, it will be added + * + * @param listName the name of the RMConstraintList + * @param authorityName + * @param values + */ + public void updateRMConstraintListAuthority(String listName, String authorityName, Listvalues); + + /** + * Remove an authority from a list + * + * @param listName the name of the RMConstraintList + * @param authorityName + * @param values + */ + public void removeRMConstraintListAuthority(String listName, String authorityName); + + /** + * Replace the values for an authority in a list. + * The existing values are removed. + * + * If the authority does not already exist in the list, it will be added + * + * @param listName the name of the RMConstraintList + * @param value + * @param authorities + */ + public void updateRMConstraintListValue(String listName, String value, Listauthorities); + + /** + * Remove an authority from a list + * + * @param listName the name of the RMConstraintList + * @param authorityName + * @param value + */ + public void removeRMConstraintListValue(String listName, String valueName); + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/RMCaveatConfigServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/RMCaveatConfigServiceImpl.java new file mode 100644 index 0000000000..33f13f4dc0 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/RMCaveatConfigServiceImpl.java @@ -0,0 +1,427 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.caveat; + +import java.io.File; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService; +import org.alfresco.module.org_alfresco_module_rm.caveat.RMListOfValuesConstraint.MatchLogic; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.service.cmr.dictionary.Constraint; +import org.alfresco.service.cmr.dictionary.ConstraintDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * RM Caveat Config Service impl + * + * @author janv + */ +public class RMCaveatConfigServiceImpl implements RMCaveatConfigService +{ + private static Log logger = LogFactory.getLog(RMCaveatConfigServiceImpl.class); + + private NamespaceService namespaceService; + private DictionaryService dictionaryService; + + private RMCaveatConfigComponent rmCaveatConfigComponent; + private RecordsManagementAdminService recordsManagementAdminService; + + + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + public void setCaveatConfigComponent(RMCaveatConfigComponent rmCaveatConfigComponent) + { + this.rmCaveatConfigComponent = rmCaveatConfigComponent; + } + + public void setRecordsManagementAdminService(RecordsManagementAdminService recordsManagementAdminService) + { + this.recordsManagementAdminService = recordsManagementAdminService; + } + + public RecordsManagementAdminService getRecordsManagementAdminService() + { + return recordsManagementAdminService; + } + + public void init() + { + rmCaveatConfigComponent.init(); + } + + public NodeRef updateOrCreateCaveatConfig(InputStream is) + { + return rmCaveatConfigComponent.updateOrCreateCaveatConfig(is); + } + + public NodeRef updateOrCreateCaveatConfig(File jsonFile) + { + return rmCaveatConfigComponent.updateOrCreateCaveatConfig(jsonFile); + } + + public NodeRef updateOrCreateCaveatConfig(String jsonString) + { + return rmCaveatConfigComponent.updateOrCreateCaveatConfig(jsonString); + } + + // Get allowed values for given caveat (for current user) + public List getRMAllowedValues(String constraintName) + { + return rmCaveatConfigComponent.getRMAllowedValues(constraintName); + } + + /** + * Check whether access to 'record component' node is vetoed for current user due to caveat(s) + * + * @param nodeRef + * @return false, if caveat(s) veto access otherwise return true + */ + public boolean hasAccess(NodeRef nodeRef) + { + return rmCaveatConfigComponent.hasAccess(nodeRef); + } + + /** + * add RM constraint list + * @param listName the name of the RMConstraintList + */ + public RMConstraintInfo addRMConstraint(String listName, String title, String[] values) + { + return addRMConstraint(listName, title, values, MatchLogic.AND); + } + + public RMConstraintInfo addRMConstraint(String listName, String title, String[] values, MatchLogic matchLogic) + { + if(listName == null) + { + // Generate a list name + // FIXME: hardcoded namespace + listName = "rmc:" + UUID.randomUUID().toString(); + } + + ListallowedValues = new ArrayList(); + for(String value : values) + { + allowedValues.add(value); + } + + QName listQName = QName.createQName(listName, namespaceService); + + // TEMP review - if it already exists then change it for now + try + { + recordsManagementAdminService.addCustomConstraintDefinition(listQName, title, true, allowedValues, matchLogic); + } + catch (AlfrescoRuntimeException e) + { + if (e.getMessage().contains("Constraint already exists")) + { + recordsManagementAdminService.changeCustomConstraintValues(listQName, allowedValues); + recordsManagementAdminService.changeCustomConstraintTitle(listQName, title); + } + } + + rmCaveatConfigComponent.addRMConstraint(listName); + + RMConstraintInfo info = new RMConstraintInfo(); + info.setName(listQName.toPrefixString()); + info.setTitle(title); + info.setAllowedValues(values); + info.setCaseSensitive(true); + return info; + } + + /** + * delete RM Constraint List + * + * @param listName the name of the RMConstraintList + */ + public void deleteRMConstraint(String listName) + { + rmCaveatConfigComponent.deleteRMConstraint(listName); + + QName listQName = QName.createQName(listName, namespaceService); + + recordsManagementAdminService.removeCustomConstraintDefinition(listQName); + } + + /** + * Add a single value to an authority in a list. The existing values of the list remain. + * + * @param listName the name of the RMConstraintList + * @param authorityName + * @param values + * @throws AlfrescoRuntimeException if either the list or the authority do not already exist. + */ + public void addRMConstraintListValue(String listName, String authorityName, String value) + { + rmCaveatConfigComponent.addRMConstraintListValue(listName, authorityName, value); + } + + /** + * Get the details of the specified list + * @param listName + * @return the details of the specified list + */ + public Map> getListDetails(String listName) + { + return rmCaveatConfigComponent.getListDetails(listName); + } + + /** + * Replace the values for an authority in a list. + * The existing values are removed. + * + * If the authority does not already exist in the list, it will be added + * + * @param listName the name of the RMConstraintList + * @param authorityName + * @param values + */ + public void updateRMConstraintListAuthority(String listName, String authorityName, Listvalues) + { + rmCaveatConfigComponent.updateRMConstraintListAuthority(listName, authorityName, values); + } + + /** + * Replace the authorities for a value in a list + * + * @param listName + * @param valueName + * @param authorities + */ + public void updateRMConstraintListValue(String listName, String valueName, Listauthorities) + { + rmCaveatConfigComponent.updateRMConstraintListValue(listName, valueName, authorities); + } + + /** + * Remove an authority from a list + * + * @param listName the name of the RMConstraintList + * @param authorityName + * @param values + */ + public void removeRMConstraintListAuthority(String listName, String authorityName) + { + rmCaveatConfigComponent.removeRMConstraintListAuthority(listName, authorityName); + } + + /** + * Get all Constraint Lists + */ + public Set getAllRMConstraints() + { + Set info = new HashSet(); + + List defs = new ArrayList(10); + for (QName caveatModelQName : rmCaveatConfigComponent.getRMCaveatModels()) + { + defs.addAll(recordsManagementAdminService.getCustomConstraintDefinitions(caveatModelQName)); + } + + for(ConstraintDefinition dictionaryDef : defs) + { + Constraint con = dictionaryDef.getConstraint(); + if (con instanceof RMListOfValuesConstraint) + { + final RMListOfValuesConstraint def = (RMListOfValuesConstraint)con; + RMConstraintInfo i = new RMConstraintInfo(); + i.setName(def.getShortName()); + i.setTitle(def.getTitle()); + + // note: assumes only one caveat/LOV against a given property + List allowedValues = AuthenticationUtil.runAs(new RunAsWork>() + { + public List doWork() + { + return def.getAllowedValues(); + } + }, AuthenticationUtil.getSystemUserName()); + + i.setAllowedValues(allowedValues.toArray(new String[allowedValues.size()])); + i.setCaseSensitive(def.isCaseSensitive()); + info.add(i); + } + + } + + return info; + } + + /** + * Get an RMConstraintInfo + * @param listQName + * @return the constraint or null if it does not exist + */ + public RMConstraintInfo getRMConstraint(QName listQName) + { + ConstraintDefinition dictionaryDef = dictionaryService.getConstraint(listQName); + if(dictionaryDef != null) + { + Constraint con = dictionaryDef.getConstraint(); + if (con instanceof RMListOfValuesConstraint) + { + final RMListOfValuesConstraint def = (RMListOfValuesConstraint)con; + + RMConstraintInfo info = new RMConstraintInfo(); + info.setName(listQName.toPrefixString()); + info.setTitle(con.getTitle()); + List allowedValues = AuthenticationUtil.runAs(new RunAsWork>() + { + public List doWork() + { + return def.getAllowedValues(); + } + }, AuthenticationUtil.getSystemUserName()); + + info.setAllowedValues(allowedValues.toArray(new String[allowedValues.size()])); + info.setCaseSensitive(def.isCaseSensitive()); + return info; + } + } + return null; + } + + /** + * Get RM Constraint detail. + * + * @return the constraintInfo or null + */ + public RMConstraintInfo getRMConstraint(String listName) + { + QName listQName = QName.createQName(listName, namespaceService); + return getRMConstraint(listQName); + + } + + /** + * Update The allowed values for an RM Constraint. + * + * @param listName The name of the list. + * @param allowedValues the new alowed values + * + */ + public RMConstraintInfo updateRMConstraintAllowedValues(String listName, String[] allowedValues) + { + QName listQName = QName.createQName(listName, namespaceService); + + if(allowedValues != null) + { + ListallowedValueList = new ArrayList(); + for(String value : allowedValues) + { + allowedValueList.add(value); + } + + ConstraintDefinition dictionaryDef = dictionaryService.getConstraint(listQName); + Constraint con = dictionaryDef.getConstraint(); + if (con instanceof RMListOfValuesConstraint) + { + final RMListOfValuesConstraint def = (RMListOfValuesConstraint)con; + List oldAllowedValues = AuthenticationUtil.runAs(new RunAsWork>() + { + public List doWork() + { + return def.getAllowedValues(); + } + }, AuthenticationUtil.getSystemUserName()); + + /** + * Deal with any additions + */ + for(String newValue : allowedValueList) + { + if(!oldAllowedValues.contains(newValue)) + { + // This is an addition + if(logger.isDebugEnabled()) + { + logger.debug("value added to list:" + listQName + ":" + newValue); + } + } + } + + /** + * Deal with any deletions + */ + for(String oldValue : oldAllowedValues) + { + if(!allowedValueList.contains(oldValue)) + { + // This is a deletion + if(logger.isDebugEnabled()) + { + logger.debug("value removed from list:" + listQName + ":" + oldValue); + } + removeRMConstraintListValue(listName, oldValue); + } + } + } + + recordsManagementAdminService.changeCustomConstraintValues(listQName, allowedValueList); + } + + return getRMConstraint(listName); + } + + /** + * Remove a value from a list and cascade delete. + */ + public void removeRMConstraintListValue(String listName, String valueName) + { + //TODO need to update the rm constraint definition + // recordsManagementAdminService. + + rmCaveatConfigComponent.removeRMConstraintListValue(listName, valueName); + } + + /** + * Update the title of this RM Constraint. + */ + public RMConstraintInfo updateRMConstraintTitle(String listName, String newTitle) + { + QName listQName = QName.createQName(listName, namespaceService); + + recordsManagementAdminService.changeCustomConstraintTitle(listQName, newTitle); + return getRMConstraint(listName); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/RMConstraintInfo.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/RMConstraintInfo.java new file mode 100644 index 0000000000..76d609df2a --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/RMConstraintInfo.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.caveat; + +public class RMConstraintInfo +{ + private String name; + private String title; + private boolean caseSensitive; + private String[] allowedValues; + + public void setName(String name) + { + this.name = name; + } + public String getName() + { + return name; + } + public void setTitle(String title) + { + this.title = title; + } + public String getTitle() + { + return title; + } + public void setCaseSensitive(boolean caseSensitive) + { + this.caseSensitive = caseSensitive; + } + public boolean isCaseSensitive() + { + return caseSensitive; + } + public void setAllowedValues(String[] values) + { + this.allowedValues = values; + } + public String[] getAllowedValues() + { + return allowedValues; + } + + + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/RMListOfValuesConstraint.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/RMListOfValuesConstraint.java new file mode 100644 index 0000000000..47ffa3fcbf --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/RMListOfValuesConstraint.java @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.caveat; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.repo.dictionary.constraint.ListOfValuesConstraint; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.dictionary.ConstraintException; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.repository.datatype.TypeConversionException; + +/** + * RM Constraint implementation that ensures the value is one of a constrained + * list of values. By default, this constraint is case-sensitive. + * + * @see #setAllowedValues(List) + * @see #setCaseSensitive(boolean) + * + * @author janv + */ +public class RMListOfValuesConstraint extends ListOfValuesConstraint +{ + //private static final String ERR_NO_VALUES = "d_dictionary.constraint.list_of_values.no_values"; + private static final String ERR_NON_STRING = "d_dictionary.constraint.string_length.non_string"; + private static final String ERR_INVALID_VALUE = "d_dictionary.constraint.list_of_values.invalid_value"; + + private List allowedValues; + private List allowedValuesUpper; + private MatchLogic matchLogic = MatchLogic.AND; // defined match logic used by caveat matching (default = "AND") + + public enum MatchLogic + { + AND, // closed marking - all values must match + OR; // open marking - at least one value must match + } + + // note: alternative to static init could be to use 'registered' constraint + private static RMCaveatConfigService caveatConfigService; + + public void setCaveatConfigService(RMCaveatConfigService caveatConfigService) + { + RMListOfValuesConstraint.caveatConfigService = caveatConfigService; + } + + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(80); + sb.append("RMListOfValuesConstraint") + .append("[allowedValues=").append(getAllowedValues()) + .append(", caseSensitive=").append(isCaseSensitive()) + .append(", sorted=").append(isCaseSensitive()) + .append(", matchLogic=").append(getMatchLogic()) + .append("]"); + return sb.toString(); + } + + public RMListOfValuesConstraint() + { + super(); + + // Set RM list of value constraints to be sorted by default + sorted = true; + } + + /** + * Get the allowed values. Note that these are String instances, but may + * represent non-String values. It is up to the caller to distinguish. + * + * @return Returns the values allowed + */ + @Override + public List getRawAllowedValues() + { + String runAsUser = AuthenticationUtil.getRunAsUser(); + if ((runAsUser != null) && (! runAsUser.equals(AuthenticationUtil.getSystemUserName())) && (caveatConfigService != null)) + { + List allowedForUser = caveatConfigService.getRMAllowedValues(getShortName()); // get allowed values for current user + + List filteredList = new ArrayList(allowedForUser.size()); + for (String allowed : allowedForUser) + { + if (this.allowedValues.contains(allowed)) + { + filteredList.add(allowed); + } + } + + return filteredList; + } + else + { + return this.allowedValues; + } + } + + private List getAllowedValuesUpper() + { + String runAsUser = AuthenticationUtil.getRunAsUser(); + if ((runAsUser != null) && (! runAsUser.equals(AuthenticationUtil.getSystemUserName())) && (caveatConfigService != null)) + { + List allowedForUser = caveatConfigService.getRMAllowedValues(getType()); // get allowed values for current user + + List filteredList = new ArrayList(allowedForUser.size()); + for (String allowed : allowedForUser) + { + if (this.allowedValuesUpper.contains(allowed.toUpperCase())) + { + filteredList.add(allowed); + } + } + + return filteredList; + } + else + { + return this.allowedValuesUpper; + } + } + /** + * Set the values that are allowed by the constraint. + * + * @param values a list of allowed values + */ + @SuppressWarnings("unchecked") + @Override + public void setAllowedValues(List allowedValues) + { + if (allowedValues == null) + { + allowedValues = new ArrayList(0); + } + int valueCount = allowedValues.size(); + this.allowedValues = Collections.unmodifiableList(allowedValues); + + // make the upper case versions + this.allowedValuesUpper = new ArrayList(valueCount); + for (String allowedValue : this.allowedValues) + { + allowedValuesUpper.add(allowedValue.toUpperCase()); + } + } + + @Override + public void initialize() + { + checkPropertyNotNull("allowedValues", allowedValues); + } + + @Override + public Map getParameters() + { + Map params = new HashMap(2); + + params.put("caseSensitive", isCaseSensitive()); + params.put("allowedValues", getAllowedValues()); + params.put("sorted", isSorted()); + params.put("matchLogic", getMatchLogic()); + + return params; + } + + public MatchLogic getMatchLogicEnum() + { + return matchLogic; + } + + public String getMatchLogic() + { + return matchLogic.toString(); + } + + public void setMatchLogic(String matchLogicStr) + { + this.matchLogic = MatchLogic.valueOf(matchLogicStr); + } + + @Override + protected void evaluateSingleValue(Object value) + { + // convert the value to a String + String valueStr = null; + try + { + valueStr = DefaultTypeConverter.INSTANCE.convert(String.class, value); + } + catch (TypeConversionException e) + { + throw new ConstraintException(ERR_NON_STRING, value); + } + // check that the value is in the set of allowed values + if (isCaseSensitive()) + { + if (!getAllowedValues().contains(valueStr)) + { + throw new ConstraintException(ERR_INVALID_VALUE, value); + } + } + else + { + if (!getAllowedValuesUpper().contains(valueStr.toUpperCase())) + { + throw new ConstraintException(ERR_INVALID_VALUE, value); + } + } + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/ScriptAuthority.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/ScriptAuthority.java new file mode 100644 index 0000000000..b615d1434a --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/ScriptAuthority.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.caveat; + +import java.io.Serializable; + + +public class ScriptAuthority implements Serializable +{ + /** + * + */ + private static final long serialVersionUID = 1L; + private String authorityTitle; + private String authorityName; + + public void setAuthorityName(String authorityName) + { + this.authorityName = authorityName; + } + public String getAuthorityName() + { + return authorityName; + } + public void setAuthorityTitle(String authorityName) + { + this.authorityTitle = authorityName; + } + public String getAuthorityTitle() + { + return authorityTitle; + } + + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/ScriptConstraint.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/ScriptConstraint.java new file mode 100644 index 0000000000..2c3598d20d --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/ScriptConstraint.java @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.caveat; + +import java.io.Serializable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.List; +import java.util.Set; + +import org.alfresco.service.cmr.security.AuthorityService; +import org.json.JSONArray; +import org.json.JSONObject; + +public class ScriptConstraint implements Serializable +{ + /** + * + */ + private static final long serialVersionUID = 1L; + + private RMConstraintInfo info; + + private RMCaveatConfigService rmCaveatconfigService; + + private AuthorityService authorityService; + + ScriptConstraint(RMConstraintInfo info, RMCaveatConfigService rmCaveatconfigService, AuthorityService authorityService) + { + this.info = info; + this.rmCaveatconfigService = rmCaveatconfigService; + this.authorityService = authorityService; + } + + public void setTitle(String title) + { + info.setTitle(title); + } + public String getTitle() + { + return info.getTitle(); + } + public void setName(String name) + { + info.setName(name); + } + + public String getName() + { + String xxx = info.getName().replace(":", "_"); + return xxx; + } + + public boolean isCaseSensitive() + { + return info.isCaseSensitive(); + } + + public String[] getAllowedValues() + { + return info.getAllowedValues(); + } + + public ScriptConstraintAuthority[] getAuthorities() + { + Map> values = rmCaveatconfigService.getListDetails(info.getName()); + + if (values == null) + { + return new ScriptConstraintAuthority[0]; + } + + // Here with some data to return + Set authorities = values.keySet(); + + ArrayList constraints = new ArrayList(values.size()); + for(String authority : authorities) + { + ScriptConstraintAuthority constraint = new ScriptConstraintAuthority(); + constraint.setAuthorityName(authority); + constraint.setValues(values.get(authority)); + constraints.add(constraint); + } + + ScriptConstraintAuthority[] retVal = constraints.toArray(new ScriptConstraintAuthority[constraints.size()]); + + return retVal; + } + + /** + * updateTitle + */ + public void updateTitle(String newTitle) + { + info.setTitle(newTitle); + rmCaveatconfigService.updateRMConstraintTitle(info.getName(), newTitle) ; + } + + /** + * updateAllowedValues + */ + public void updateAllowedValues(String[] allowedValues) + { + info.setAllowedValues(allowedValues); + rmCaveatconfigService.updateRMConstraintAllowedValues(info.getName(), allowedValues); + } + + /** + * Update a value + * @param values + * @param authorities + */ + public void updateValues(JSONArray bodge) throws Exception + { + for(int i = 0; i < bodge.length(); i++) + { + + JSONObject obj = bodge.getJSONObject(i); + String value = obj.getString("value"); + JSONArray authorities = obj.getJSONArray("authorities"); + List aList = new ArrayList(); + for(int j = 0; j < authorities.length();j++) + { + aList.add(authorities.getString(j)); + } + rmCaveatconfigService.updateRMConstraintListValue(info.getName(), value, aList); + } + } + + /** + * Update a value + * @param values + * @param authorities + */ + public void updateValues(String value, String[] authorities) + { + List list = Arrays.asList(authorities); + rmCaveatconfigService.updateRMConstraintListValue(info.getName(), value, list); + } + + /** + * Cascade delete an authority + * @param authority + */ + public void deleteAuthority(String authority) + { + + } + + /** + * Cascade delete a value + * @param value + */ + public void deleteValue(String value) + { + + } + + + /** + * Get a single value + * @param value + * @return + */ + public ScriptConstraintValue getValue(String value) + { + ScriptConstraintValue[] values = getValues(); + + for(ScriptConstraintValue val : values) + { + if(val.getValueName().equalsIgnoreCase(value)) + { + return val; + } + } + return null; + } + + public ScriptConstraintValue[] getValues() + { + // authority, values + Map> details = rmCaveatconfigService.getListDetails(info.getName()); + + if (details == null) + { + details = new HashMap>(); + } + + // values, authorities + Map> pivot = PivotUtil.getPivot(details); + + // Here with some data to return + Set values = pivot.keySet(); + + ArrayList constraints = new ArrayList(pivot.size()); + for(String value : values) + { + ScriptConstraintValue constraint = new ScriptConstraintValue(); + constraint.setValueName(value); + constraint.setValueTitle(value); + + Listauthorities = pivot.get(value); + List sauth = new ArrayList(); + for(String authority : authorities) + { + ScriptAuthority a = new ScriptAuthority(); + a.setAuthorityName(authority); + + String displayName = authorityService.getAuthorityDisplayName(authority); + if(displayName != null) + { + a.setAuthorityTitle(displayName); + } + else + { + a.setAuthorityTitle(authority); + } + sauth.add(a); + } + constraint.setAuthorities(sauth); + constraints.add(constraint); + } + + /** + * Now go through and add any "empty" values + */ + for(String value : info.getAllowedValues()) + { + if(!values.contains(value)) + { + ScriptConstraintValue constraint = new ScriptConstraintValue(); + constraint.setValueName(value); + constraint.setValueTitle(value); + List sauth = new ArrayList(); + constraint.setAuthorities(sauth); + constraints.add(constraint); + } + } + + + ScriptConstraintValue[] retVal = constraints.toArray(new ScriptConstraintValue[constraints.size()]); + return retVal; + } + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/ScriptConstraintAuthority.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/ScriptConstraintAuthority.java new file mode 100644 index 0000000000..47d4c2a139 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/ScriptConstraintAuthority.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.caveat; + +import java.util.List; +import java.io.Serializable; + +public class ScriptConstraintAuthority implements Serializable +{ + /** + * + */ + private static final long serialVersionUID = -4659454215122271811L; + private String authorityName; + private Listvalues; + + public void setValues(List values) + { + this.values = values; + } + public List getValues() + { + return values; + } + public void setAuthorityName(String authorityName) + { + this.authorityName = authorityName; + } + public String getAuthorityName() + { + return authorityName; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/ScriptConstraintValue.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/ScriptConstraintValue.java new file mode 100644 index 0000000000..7c7db55a7f --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/ScriptConstraintValue.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.caveat; + +import java.util.List; +import java.io.Serializable; + +public class ScriptConstraintValue implements Serializable +{ + /** + * + */ + private static final long serialVersionUID = -4659454215122271811L; + private String value; + private Listauthorities; + + public void setAuthorities(List values) + { + this.authorities = values; + } + public List getAuthorities() + { + return authorities; + } + public void setValueName(String authorityName) + { + this.value = authorityName; + } + public String getValueName() + { + return value; + } + public void setValueTitle(String authorityName) + { + this.value = authorityName; + } + public String getValueTitle() + { + return value; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/ScriptRMCaveatConfigService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/ScriptRMCaveatConfigService.java new file mode 100644 index 0000000000..740b72a546 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/ScriptRMCaveatConfigService.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.caveat; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.alfresco.repo.jscript.BaseScopableProcessorExtension; +import org.alfresco.service.cmr.security.AuthorityService; + +/** + * Script projection of RM Caveat Config Service + * + * @author Mark Rogers + */ +public class ScriptRMCaveatConfigService extends BaseScopableProcessorExtension +{ + private RMCaveatConfigService caveatConfigService; + private AuthorityService authorityService; + + public void setCaveatConfigService(RMCaveatConfigService rmCaveatConfigService) + { + this.caveatConfigService = rmCaveatConfigService; + } + + public RMCaveatConfigService getRmCaveatConfigService() + { + return caveatConfigService; + } + + public void setAuthorityService(AuthorityService authorityService) + { + this.authorityService = authorityService; + } + + public AuthorityService getAuthorityService() + { + return authorityService; + } + + public ScriptConstraint getConstraint(String listName) + { + //TODO Temporary conversion + String xxx = listName.replace("_", ":"); + + RMConstraintInfo info = caveatConfigService.getRMConstraint(xxx); + + if(info != null) + { + return new ScriptConstraint(info, caveatConfigService, getAuthorityService()); + } + + return null; + } + + public ScriptConstraint[] getAllConstraints() + { + return getConstraints(true); + } + + public ScriptConstraint[] getConstraintsWithoutEmptyList() + { + return getConstraints(false); + } + + private ScriptConstraint[] getConstraints(boolean includeEmptyList) + { + Set values = caveatConfigService.getAllRMConstraints(); + + List vals = new ArrayList(values.size()); + for(RMConstraintInfo value : values) + { + ScriptConstraint c = new ScriptConstraint(value, caveatConfigService, getAuthorityService()); + if (includeEmptyList) + { + vals.add(c); + } + else + { + if (c.getValues().length > 0) + { + vals.add(c); + } + } + } + + return vals.toArray(new ScriptConstraint[vals.size()]); + } + + /** + * Delete list + * @param listName + + */ + public void deleteConstraintList(String listName) + { + //TODO Temporary conversion + String xxx = listName.replace("_", ":"); + caveatConfigService.deleteRMConstraint(xxx); + } + + + + /** + * Update value + */ + public void updateConstraintValues(String listName, String authorityName, String[]values) + { + List vals = new ArrayList(); + caveatConfigService.updateRMConstraintListAuthority(listName, authorityName, vals); + } + + /** + * Delete the constraint values. i.e remove an authority from a constraint list + */ + public void deleteRMConstraintListAuthority(String listName, String authorityName) + { + //TODO Temporary conversion + String xxx = listName.replace("_", ":"); + + caveatConfigService.removeRMConstraintListAuthority(xxx, authorityName); + } + + /** + * Delete the constraint values. i.e remove a value from a constraint list + */ + public void deleteRMConstraintListValue(String listName, String valueName) + { + //TODO Temporary conversion + String xxx = listName.replace("_", ":"); + + caveatConfigService.removeRMConstraintListValue(xxx, valueName); + + } + + public ScriptConstraint createConstraint(String listName, String title, String[] allowedValues) + { + //TODO Temporary conversion + if(listName != null) + { + listName = listName.replace("_", ":"); + } + + RMConstraintInfo info = caveatConfigService.addRMConstraint(listName, title, allowedValues); + ScriptConstraint c = new ScriptConstraint(info, caveatConfigService, getAuthorityService()); + return c; + } + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionAction.java new file mode 100644 index 0000000000..212c8d199b --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionAction.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.disposition; + +import java.util.Date; +import java.util.List; + +import org.alfresco.module.org_alfresco_module_rm.event.EventCompletionDetails; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Disposition action interface. + * + * @author Roy Wetherall + */ +public interface DispositionAction +{ + /** + * @return the node reference + */ + NodeRef getNodeRef(); + + /** + * @return the disposition action definition + */ + DispositionActionDefinition getDispositionActionDefinition(); + + /** + * @return the id of the action + */ + String getId(); + + /** + * @return the name of the action + */ + String getName(); + + /** + * @return the display label for the action + */ + String getLabel(); + + /** + * @return the dispostion action as of eligibility date + */ + Date getAsOfDate(); + + /** + * @return true if the events are complete (ie: enough events have been completed to make the disposition + * action + */ + boolean isEventsEligible(); + + /** + * @return the user that started the action + */ + String getStartedBy(); + + /** + * @return when the action was started + */ + Date getStartedAt(); + + /** + * @return the user that completed the action + */ + String getCompletedBy(); + + /** + * @return when the action was completed + */ + Date getCompletedAt(); + + /** + * @return List of events that need to be completed for the action + */ + List getEventCompletionDetails(); +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionActionDefinition.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionActionDefinition.java new file mode 100644 index 0000000000..bdac5e0218 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionActionDefinition.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.disposition; + +import java.util.List; + +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEvent; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.Period; +import org.alfresco.service.namespace.QName; + +/** + * Disposition action interface + * + * @author Roy Wetherall + */ +public interface DispositionActionDefinition +{ + /** + * Get the NodeRef that represents the disposition action definition + * + * @return NodeRef of disposition action definition + */ + NodeRef getNodeRef(); + + /** + * Get disposition action id + * + * @return String id + */ + String getId(); + + /** + * Get the index of the action within the disposition instructions + * + * @return int disposition action index + */ + int getIndex(); + + /** + * Get the name of disposition action + * + * @return String name + */ + String getName(); + + /** + * Get the display label of the disposition action + * + * @return String name's display label + */ + String getLabel(); + + /** + * Get the description of the disposition action + * + * @return String description + */ + String getDescription(); + + /** + * Get the period for the disposition action + * + * @return Period disposition period + */ + Period getPeriod(); + + /** + * Property to which the period is relative to + * + * @return QName property name + */ + QName getPeriodProperty(); + + /** + * List of events for the disposition + * + * @return List list of events + */ + List getEvents(); + + /** + * Indicates whether the disposition action is eligible when the earliest event is complete, otherwise + * all events must be complete before eligibility. + * + * @return boolean true if eligible on first action complete, false otherwise + */ + boolean eligibleOnFirstCompleteEvent(); + + /** + * Get the location of the disposition (can be null) + * + * @return String disposition location + */ + String getLocation(); +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionActionDefinitionImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionActionDefinitionImpl.java new file mode 100644 index 0000000000..e6d4b4061b --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionActionDefinitionImpl.java @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.disposition; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEvent; +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.Period; +import org.alfresco.service.namespace.QName; + +/** + * Disposition action implementation + * + * @author Roy Wetherall + */ +public class DispositionActionDefinitionImpl implements DispositionActionDefinition, RecordsManagementModel +{ + /** Name */ + private String name; + + /** Description */ + private String description; + + /** Label */ + private String label; + + /** Node service */ + private NodeService nodeService; + + /** Records management action service */ + private RecordsManagementActionService recordsManagementActionService; + + /** Records management event service */ + private RecordsManagementEventService recordsManagementEventService; + + /** Disposition action node reference */ + private NodeRef dispositionActionNodeRef; + + /** Action index */ + private int index; + + /** + * Constructor + * + * @param services service registry + * @param nodeRef disposition action node reference + * @param index index of disposition action + */ + public DispositionActionDefinitionImpl(RecordsManagementEventService recordsManagementEventService, RecordsManagementActionService recordsManagementActionService, NodeService nodeService, NodeRef nodeRef, int index) + { + //this.services = services; + this.recordsManagementEventService = recordsManagementEventService; + this.recordsManagementActionService = recordsManagementActionService; + this.nodeService = nodeService; + this.dispositionActionNodeRef = nodeRef; + this.index = index; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition#getNodeRef() + */ + public NodeRef getNodeRef() + { + return this.dispositionActionNodeRef; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition#getIndex() + */ + public int getIndex() + { + return this.index; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition#getId() + */ + public String getId() + { + return this.dispositionActionNodeRef.getId(); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition#getDescription() + */ + public String getDescription() + { + if (description == null) + { + description = (String)nodeService.getProperty(this.dispositionActionNodeRef, PROP_DISPOSITION_DESCRIPTION); + } + return description; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition#getName() + */ + public String getName() + { + if (name == null) + { + name = (String)nodeService.getProperty(this.dispositionActionNodeRef, PROP_DISPOSITION_ACTION_NAME); + } + return name; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition#getLabel() + */ + public String getLabel() + { + if (label == null) + { + String name = getName(); + label = name; + + // get the disposition action from the RM action service + RecordsManagementAction action = recordsManagementActionService.getDispositionAction(name); + if (action != null) + { + label = action.getLabel(); + } + } + + return label; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition#getPeriod() + */ + public Period getPeriod() + { + return (Period)nodeService.getProperty(this.dispositionActionNodeRef, PROP_DISPOSITION_PERIOD); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition#getPeriodProperty() + */ + public QName getPeriodProperty() + { + QName result = null; + String value = (String)nodeService.getProperty(this.dispositionActionNodeRef, PROP_DISPOSITION_PERIOD_PROPERTY); + if (value != null) + { + result = QName.createQName(value); + } + return result; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition#getEvents() + */ + @SuppressWarnings("unchecked") + public List getEvents() + { + List events = null; + Collection eventNames = (Collection)nodeService.getProperty(this.dispositionActionNodeRef, PROP_DISPOSITION_EVENT); + if (eventNames != null) + { + events = new ArrayList(eventNames.size()); + for (String eventName : eventNames) + { + RecordsManagementEvent event = recordsManagementEventService.getEvent(eventName); + events.add(event); + } + } + else + { + events = java.util.Collections.EMPTY_LIST; + } + return events; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition#eligibleOnFirstCompleteEvent() + */ + public boolean eligibleOnFirstCompleteEvent() + { + boolean result = true; + String value = (String)nodeService.getProperty(this.dispositionActionNodeRef, PROP_DISPOSITION_EVENT_COMBINATION); + if (value != null && value.equals("and") == true) + { + result = false; + } + return result; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition#getLocation() + */ + public String getLocation() + { + return (String)nodeService.getProperty(this.dispositionActionNodeRef, PROP_DISPOSITION_LOCATION); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionActionImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionActionImpl.java new file mode 100644 index 0000000000..bff4a0cc68 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionActionImpl.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.disposition; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction; +import org.alfresco.module.org_alfresco_module_rm.event.EventCompletionDetails; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; + +/** + * @author Roy Wetherall + */ +public class DispositionActionImpl implements DispositionAction, + RecordsManagementModel +{ + private RecordsManagementServiceRegistry services; + private NodeRef dispositionNodeRef; + private DispositionActionDefinition dispositionActionDefinition; + + /** + * Constructor + * + * @param services + * @param dispositionActionNodeRef + */ + public DispositionActionImpl(RecordsManagementServiceRegistry services, NodeRef dispositionActionNodeRef) + { + this.services = services; + this.dispositionNodeRef = dispositionActionNodeRef; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction#getDispositionActionDefinition() + */ + public DispositionActionDefinition getDispositionActionDefinition() + { + if (this.dispositionActionDefinition == null) + { + // Get the current action + String id = (String)services.getNodeService().getProperty(this.dispositionNodeRef, PROP_DISPOSITION_ACTION_ID); + + // Get the disposition instructions for the owning node + NodeRef recordNodeRef = this.services.getNodeService().getPrimaryParent(this.dispositionNodeRef).getParentRef(); + if (recordNodeRef != null) + { + DispositionSchedule ds = this.services.getDispositionService().getDispositionSchedule(recordNodeRef); + + if (ds != null) + { + // Get the disposition action definition + this.dispositionActionDefinition = ds.getDispositionActionDefinition(id); + } + } + } + + return this.dispositionActionDefinition; + + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction#getNodeRef() + */ + public NodeRef getNodeRef() + { + return this.dispositionNodeRef; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction#getLabel() + */ + public String getLabel() + { + String name = getName(); + String label = name; + + // get the disposition action from the RM action service + RecordsManagementAction action = this.services.getRecordsManagementActionService().getDispositionAction(name); + if (action != null) + { + label = action.getLabel(); + } + + return label; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction#getId() + */ + public String getId() + { + return (String)this.services.getNodeService().getProperty(this.dispositionNodeRef, PROP_DISPOSITION_ACTION_ID); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction#getName() + */ + public String getName() + { + return (String)this.services.getNodeService().getProperty(this.dispositionNodeRef, PROP_DISPOSITION_ACTION); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction#getAsOfDate() + */ + public Date getAsOfDate() + { + return (Date)this.services.getNodeService().getProperty(this.dispositionNodeRef, PROP_DISPOSITION_AS_OF); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction#isEventsEligible() + */ + public boolean isEventsEligible() + { + return ((Boolean)this.services.getNodeService().getProperty(this.dispositionNodeRef, PROP_DISPOSITION_EVENTS_ELIGIBLE)).booleanValue(); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction#getCompletedAt() + */ + public Date getCompletedAt() + { + return (Date)this.services.getNodeService().getProperty(this.dispositionNodeRef, PROP_DISPOSITION_ACTION_COMPLETED_AT); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction#getCompletedBy() + */ + public String getCompletedBy() + { + return (String)this.services.getNodeService().getProperty(this.dispositionNodeRef, PROP_DISPOSITION_ACTION_COMPLETED_BY); + } + + /* + * @see org.alfresco.module.org_alfresco_module_rm.DispositionAction#getStartedAt() + */ + public Date getStartedAt() + { + return (Date)this.services.getNodeService().getProperty(this.dispositionNodeRef, PROP_DISPOSITION_ACTION_STARTED_AT); + } + + /* + * @see org.alfresco.module.org_alfresco_module_rm.DispositionAction#getStartedBy() + */ + public String getStartedBy() + { + return (String)this.services.getNodeService().getProperty(this.dispositionNodeRef, PROP_DISPOSITION_ACTION_STARTED_BY); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction#getEventCompletionDetails() + */ + public List getEventCompletionDetails() + { + List assocs = this.services.getNodeService().getChildAssocs( + this.dispositionNodeRef, + ASSOC_EVENT_EXECUTIONS, + RegexQNamePattern.MATCH_ALL); + List result = new ArrayList(assocs.size()); + for (ChildAssociationRef assoc : assocs) + { + Map props = this.services.getNodeService().getProperties(assoc.getChildRef()); + String eventName = (String)props.get(PROP_EVENT_EXECUTION_NAME); + EventCompletionDetails ecd = new EventCompletionDetails( + assoc.getChildRef(), eventName, + this.services.getRecordsManagementEventService().getEvent(eventName).getDisplayLabel(), + getBooleanValue(props.get(PROP_EVENT_EXECUTION_AUTOMATIC), false), + getBooleanValue(props.get(PROP_EVENT_EXECUTION_COMPLETE), false), + (Date)props.get(PROP_EVENT_EXECUTION_COMPLETED_AT), + (String)props.get(PROP_EVENT_EXECUTION_COMPLETED_BY)); + result.add(ecd); + } + + return result; + } + + /** + * Helper method to deal with boolean values + * + * @param value + * @param defaultValue + * @return + */ + private boolean getBooleanValue(Object value, boolean defaultValue) + { + boolean result = defaultValue; + if (value != null && value instanceof Boolean) + { + result = ((Boolean)value).booleanValue(); + } + return result; + } + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionPeriodProperties.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionPeriodProperties.java new file mode 100644 index 0000000000..2479e6f06f --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionPeriodProperties.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.disposition; + +import java.util.ArrayList; +import java.util.List; + +import org.alfresco.service.namespace.QName; + +/** + * Spring bean to allow configuration of properties used for calculating + * dates in disposition schedules. + * + * @author Gavin Cornwell + */ +public class DispositionPeriodProperties +{ + public static final String BEAN_NAME = "DispositionPeriodProperties"; + + private List periodProperties; + + public void setPropertyList(List propertyList) + { + periodProperties = new ArrayList(propertyList.size()); + for (String property : propertyList) + { + periodProperties.add(QName.createQName(property)); + } + } + + public List getPeriodProperties() + { + return periodProperties; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionSchedule.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionSchedule.java new file mode 100644 index 0000000000..c46177deb3 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionSchedule.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.disposition; + +import java.util.List; + +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Disposition schedule interface + * + * @author Roy Wetherall + */ +public interface DispositionSchedule +{ + /** + * Get the NodeRef that represents the disposition schedule + * + * @return {@link NodeRef} of disposition schedule + */ + NodeRef getNodeRef(); + + /** + * Get the disposition authority + * + * @return {@link String} disposition authority + */ + String getDispositionAuthority(); + + /** + * Get the disposition instructions + * + * @return {@link String} disposition instructions + */ + String getDispositionInstructions(); + + /** + * Indicates whether the disposal occurs at record level or not + * + * @return boolean true if at record level, false otherwise + */ + boolean isRecordLevelDisposition(); + + /** + * Gets all the disposition action definitions for the schedule + * + * @return List<{@link DispositionActionDefinition}> disposition action definitions + */ + List getDispositionActionDefinitions(); + + /** + * Get the disposition action definition + * + * @param id the action definition id + * @return {@link DispositionActionDefinition} disposition action definition + */ + DispositionActionDefinition getDispositionActionDefinition(String id); + + /** + * Get the disposition action definition by the name of the disposition action + * + * @param name disposition action name + * @return {@link DispositionActionDefinition} disposition action definition, null if none + */ + DispositionActionDefinition getDispositionActionDefinitionByName(String name); +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionScheduleImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionScheduleImpl.java new file mode 100644 index 0000000000..242357570b --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionScheduleImpl.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.disposition; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.RegexQNamePattern; + +/** + * Disposition instructions implementation + * + * @author Roy Wetherall + */ +public class DispositionScheduleImpl implements DispositionSchedule, + RecordsManagementModel +{ + private NodeService nodeService; + private RecordsManagementServiceRegistry services; + private NodeRef dispositionDefinitionNodeRef; + + private List actions; + private Map actionsById; + + //If name is not the same as node-uuid, then action will be stored here too + //Fix for ALF-2588 + private Map actionsByName; + + /** Map of disposition definitions by disposition action name */ + private Map actionsByDispositionActionName; + + public DispositionScheduleImpl(RecordsManagementServiceRegistry services, NodeService nodeService, NodeRef nodeRef) + { + // TODO check that we have a disposition definition node reference + + this.dispositionDefinitionNodeRef = nodeRef; + this.nodeService = nodeService; + this.services = services; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule#getNodeRef() + */ + public NodeRef getNodeRef() + { + return this.dispositionDefinitionNodeRef; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule#getDispositionAuthority() + */ + public String getDispositionAuthority() + { + return (String)this.nodeService.getProperty(this.dispositionDefinitionNodeRef, PROP_DISPOSITION_AUTHORITY); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule#getDispositionInstructions() + */ + public String getDispositionInstructions() + { + return (String)this.nodeService.getProperty(this.dispositionDefinitionNodeRef, PROP_DISPOSITION_INSTRUCTIONS); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule#isRecordLevelDisposition() + */ + public boolean isRecordLevelDisposition() + { + boolean result = false; + Boolean value = (Boolean)this.nodeService.getProperty(this.dispositionDefinitionNodeRef, PROP_RECORD_LEVEL_DISPOSITION); + if (value != null) + { + result = value.booleanValue(); + } + return result; + } + + /** + * Get disposition action definition + * + * @param id action definition identifier + * @return DispositionActionDefinition disposition action definition + */ + public DispositionActionDefinition getDispositionActionDefinition(String id) + { + if (this.actions == null) + { + getDispositionActionsImpl(); + } + + DispositionActionDefinition actionDef = this.actionsById.get(id); + if (actionDef == null) + { + actionDef = this.actionsByName.get(id); + } + return actionDef; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule#getDispositionActionDefinitionByName(java.lang.String) + */ + @Override + public DispositionActionDefinition getDispositionActionDefinitionByName(String name) + { + if (this.actionsByDispositionActionName == null) + { + getDispositionActionsImpl(); + } + return actionsByDispositionActionName.get(name); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule#getDispositionActionDefinitions() + */ + public List getDispositionActionDefinitions() + { + if (this.actions == null) + { + getDispositionActionsImpl(); + } + + return this.actions; + } + + /** + * Get the disposition actions into the local cache + */ + private void getDispositionActionsImpl() + { + List assocs = this.nodeService.getChildAssocs( + this.dispositionDefinitionNodeRef, + ASSOC_DISPOSITION_ACTION_DEFINITIONS, + RegexQNamePattern.MATCH_ALL); + this.actions = new ArrayList(assocs.size()); + this.actionsById = new HashMap(assocs.size()); + this.actionsByName = new HashMap(assocs.size()); + this.actionsByDispositionActionName = new HashMap(assocs.size()); + int index = 0; + for (ChildAssociationRef assoc : assocs) + { + DispositionActionDefinition da = new DispositionActionDefinitionImpl(services.getRecordsManagementEventService(), services.getRecordsManagementActionService(), nodeService, assoc.getChildRef(), index); + actions.add(da); + actionsById.put(da.getId(), da); + index++; + + String actionNodeName = (String) nodeService.getProperty(assoc.getChildRef(), ContentModel.PROP_NAME); + if (!actionNodeName.equals(da.getId())) + { + //It was imported and now has new ID. Old ID may present in old files. + actionsByName.put(actionNodeName, da); + } + + String actionDefintionName = (String)nodeService.getProperty(assoc.getChildRef(), PROP_DISPOSITION_ACTION_NAME); + if (actionDefintionName != null) + { + actionsByDispositionActionName.put(actionDefintionName, da); + } + } + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionSelectionStrategy.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionSelectionStrategy.java new file mode 100644 index 0000000000..e462725a80 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionSelectionStrategy.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.disposition; + +import java.util.Comparator; +import java.util.Date; +import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.service.cmr.repository.NodeRef; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * This class offers the default implementation of a strategy for selection of + * disposition schedule for a record when there is more than one which is applicable. + * An example of where this strategy might be used would be in the case of a record + * which was multiply filed. + * + * @author neilm + */ +public class DispositionSelectionStrategy implements RecordsManagementModel +{ + /** Logger */ + private static Log logger = LogFactory.getLog(DispositionSelectionStrategy.class); + + /** Disposition service */ + private DispositionService dispositionService; + + /** + * Set the disposition service + * + * @param dispositionService disposition service + */ + public void setDispositionService(DispositionService dispositionService) + { + this.dispositionService = dispositionService; + } + + /** + * Select the disposition schedule to use given there is more than one + * + * @param recordFolders + * @return + */ + public NodeRef selectDispositionScheduleFrom(List recordFolders) + { + if (recordFolders == null || recordFolders.isEmpty()) + { + return null; + } + else + { + // 46 CHAPTER 2 + // Records assigned more than 1 disposition must be retained and linked to the record folder (category) with the longest + // retention period. + + // Assumption: an event-based disposition action has a longer retention + // period than a time-based one - as we cannot know when an event will occur + // TODO Automatic events? + + SortedSet sortedFolders = new TreeSet(new DispositionableNodeRefComparator()); + for (NodeRef f : recordFolders) + { + sortedFolders.add(f); + } + DispositionSchedule dispSchedule = dispositionService.getDispositionSchedule(sortedFolders.first()); + + if (logger.isDebugEnabled()) + { + logger.debug("Selected disposition schedule: " + dispSchedule); + } + + NodeRef result = null; + if (dispSchedule != null) + { + result = dispSchedule.getNodeRef(); + } + return result; + } + } + + /** + * This class defines a natural comparison order between NodeRefs that have + * the dispositionLifecycle aspect applied. + * This order has the following meaning: NodeRefs with a 'lesser' value are considered + * to have a shorter retention period, although the actual retention period may + * not be straightforwardly determined in all cases. + */ + class DispositionableNodeRefComparator implements Comparator + { + public int compare(final NodeRef f1, final NodeRef f2) + { + // Run as admin user + return AuthenticationUtil.runAs(new RunAsWork() + { + public Integer doWork() throws Exception + { + return new Integer(compareImpl(f1, f2)); + } + + }, AuthenticationUtil.getAdminUserName()).intValue(); + } + + private int compareImpl(NodeRef f1, NodeRef f2) + { + //TODO Check the nodeRefs have the correct aspect + + DispositionAction da1 = dispositionService.getNextDispositionAction(f1); + DispositionAction da2 = dispositionService.getNextDispositionAction(f2); + + if (da1 != null && da2 != null) + { + Date asOfDate1 = da1.getAsOfDate(); + Date asOfDate2 = da2.getAsOfDate(); + // If both record(Folder)s have asOfDates, then use these to compare + if (asOfDate1 != null && asOfDate2 != null) + { + return asOfDate1.compareTo(asOfDate2); + } + // If one has a date and the other doesn't, the one with the date is "less". + // (Defined date is 'shorter' than undefined date as an undefined date means it may be retained forever - theoretically) + else if (asOfDate1 != null || asOfDate2 != null) + { + return asOfDate1 == null ? +1 : -1; + } + else + { + // Neither has an asOfDate. (Somewhat arbitrarily) we'll use the number of events to compare now. + DispositionActionDefinition dad1 = da1.getDispositionActionDefinition(); + DispositionActionDefinition dad2 = da2.getDispositionActionDefinition(); + int eventsCount1 = 0; + int eventsCount2 = 0; + + if (dad1 != null) + { + eventsCount1 = dad1.getEvents().size(); + } + if (dad2 != null) + { + eventsCount2 = dad2.getEvents().size(); + } + return new Integer(eventsCount1).compareTo(eventsCount2); + } + } + + return 0; + } + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionService.java new file mode 100644 index 0000000000..0d02d2a336 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionService.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.disposition; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * Disposition service + * + * @author Roy Wetherall + * @since 2.0 + */ +public interface DispositionService +{ + /** ========= Disposition Schedule Methods ========= */ + + /** + * Get the disposition schedule for a given record management node. Traverses the hierarchy to + * find the first disposition schedule in the primary hierarchy. + * + * @param nodeRef node reference to record category, record folder or record + * @return {@link DispositionSchedule} disposition schedule + */ + DispositionSchedule getDispositionSchedule(NodeRef nodeRef); + + // Gets all the disposition schedules, not just the first in the primary parent path. + // TODO List getAllDispositionSchedules(NodeRef nodeRef); + + /** + * Get the disposition schedule directly associated with the node specified. Returns + * null if none. + * + * @param nodeRef node reference + * @return {@link DispositionSchedule} disposition schedule directly associated with the node reference, null if none + */ + DispositionSchedule getAssociatedDispositionSchedule(NodeRef nodeRef); + + /** + * Gets the records management container that is directly associated with the disposition schedule. + * + * @param dispositionSchedule disposition schedule + * @return {@link NodeRef} node reference of the associated container + */ + NodeRef getAssociatedRecordsManagementContainer(DispositionSchedule dispositionSchedule); + + /** + * Indicates whether a disposition schedule has any disposable items under its management + * + * @param dispositionSchdule disposition schedule + * @return boolean true if there are disposable items being managed by, false otherwise + */ + boolean hasDisposableItems(DispositionSchedule dispositionSchdule); + + /** + * Gets a list of all the disposable items (records, record folders) that are under the control of + * the disposition schedule. + * + * @param dispositionSchedule disposition schedule + * @return {@link List}<{@link NodeRef}> list of disposable items + */ + List getDisposableItems(DispositionSchedule dispositionSchedule); + + /** + * Indicates whether the node is a disposable item or not (ie is under the control of a disposition schedule) + * + * @param nodeRef node reference + * @return boolean true if node is a disposable item, false otherwise + */ + boolean isDisposableItem(NodeRef nodeRef); + + /** + * Creates a disposition schedule on the given record category. + * + * @param recordCategory + * @param props + * @return {@link DispositionSchedule} + */ + DispositionSchedule createDispositionSchedule(NodeRef recordCategory, Map props); + + // TODO DispositionSchedule updateDispositionSchedule(DispositionScedule, Map props) + + // TODO void removeDispositionSchedule(NodeRef nodeRef); - can only remove if no disposition items + + /** ========= Disposition Action Definition Methods ========= */ + + /** + * Adds a new disposition action definition to the given disposition schedule. + * + * @param schedule The DispositionSchedule to add to + * @param actionDefinitionParams Map of parameters to use to create the action definition + */ + DispositionActionDefinition addDispositionActionDefinition( + DispositionSchedule schedule, + Map actionDefinitionParams); + + /** + * Removes the given disposition action definition from the given disposition + * schedule. + * + * @param schedule The DispositionSchedule to remove from + * @param actionDefinition The DispositionActionDefinition to remove + */ + void removeDispositionActionDefinition( + DispositionSchedule schedule, + DispositionActionDefinition actionDefinition); + + /** + * Updates the given disposition action definition belonging to the given disposition + * schedule. + * + * @param actionDefinition The DispositionActionDefinition to update + * @param actionDefinitionParams Map of parameters to use to update the action definition + * @return The updated DispositionActionDefinition + */ + DispositionActionDefinition updateDispositionActionDefinition( + DispositionActionDefinition actionDefinition, + Map actionDefinitionParams); + + + /** + * TODO MOVE THIS FROM THIS API + * + * @param nodeRef + * @return + */ + boolean isNextDispositionActionEligible(NodeRef nodeRef); + + /** ========= Disposition Action Methods ========= */ + + /** + * Gets the next disposition action for a given node + * + * @param nodeRef + * @return + */ + DispositionAction getNextDispositionAction(NodeRef nodeRef); + + + /** ========= Disposition Action History Methods ========= */ + + /** + * Gets a list of all the completed disposition action in the order they occured. + * + * @param nodeRef record/record folder + * @return List list of completed disposition actions + */ + List getCompletedDispositionActions(NodeRef nodeRef); + + /** + * Helper method to get the last completed disposition action. Returns null + * if there is none. + * + * @param nodeRef record/record folder + * @return DispositionAction last completed disposition action, null if none + */ + DispositionAction getLastCompletedDispostionAction(NodeRef nodeRef); + + /** ========= ========= */ + + /** + * Returns the list of disposition period properties + * + * @return list of disposition period properties + */ + List getDispositionPeriodProperties(); + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java new file mode 100644 index 0000000000..baddd47d32 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java @@ -0,0 +1,626 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.disposition; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.policy.BehaviourFilter; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.util.ParameterCheck; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +/** + * Disposition service implementation. + * + * @author Roy Wetherall + */ +public class DispositionServiceImpl implements DispositionService, RecordsManagementModel, ApplicationContextAware +{ + /** Logger */ + private static Log logger = LogFactory.getLog(DispositionServiceImpl.class); + + /** Node service */ + private NodeService nodeService; + + /** Dictionary service */ + private DictionaryService dictionaryService; + + /** Behaviour filter */ + private BehaviourFilter behaviourFilter; + + /** Records management service */ + private RecordsManagementService rmService; + + /** Records management service registry */ + private RecordsManagementServiceRegistry serviceRegistry; + + /** Disposition selection strategy */ + private DispositionSelectionStrategy dispositionSelectionStrategy; + + /** Application context */ + private ApplicationContext applicationContext; + + /** + * Set node service + * + * @param nodeService the node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Set the dictionary service + * + * @param dictionaryServic the dictionary service + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + /** + * Set the behaviour filter. + * + * @param behaviourFilter the behaviour filter + */ + public void setBehaviourFilter(BehaviourFilter behaviourFilter) + { + this.behaviourFilter = behaviourFilter; + } + + /** + * Set the records management service registry + * + * @param serviceRegistry records management registry service + */ + public void setRecordsManagementServiceRegistry(RecordsManagementServiceRegistry serviceRegistry) + { + this.serviceRegistry = serviceRegistry; + } + + /** + * Get the records management service + * NOTE: have to pull it out of the app context manually to prevent Spring circular dependancy issue + * + * @return + */ + public RecordsManagementService getRmService() + { + if (rmService == null) + { + rmService = (RecordsManagementService)applicationContext.getBean("recordsManagementService"); + } + return rmService; + } + + /** + * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) + */ + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException + { + this.applicationContext = applicationContext; + } + + /** + * Set the dispositionSelectionStrategy bean. + * + * @param dispositionSelectionStrategy + */ + public void setDispositionSelectionStrategy(DispositionSelectionStrategy dispositionSelectionStrategy) + { + this.dispositionSelectionStrategy = dispositionSelectionStrategy; + } + + /** ========= Disposition Schedule Methods ========= */ + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#getDispositionSchedule(org.alfresco.service.cmr.repository.NodeRef) + */ + public DispositionSchedule getDispositionSchedule(NodeRef nodeRef) + { + DispositionSchedule di = null; + NodeRef diNodeRef = null; + if (getRmService().isRecord(nodeRef) == true) + { + // Get the record folders for the record + List recordFolders = getRmService().getRecordFolders(nodeRef); + // At this point, we may have disposition instruction objects from 1..n folders. + diNodeRef = dispositionSelectionStrategy.selectDispositionScheduleFrom(recordFolders); + } + else + { + // Get the disposition instructions for the node reference provided + diNodeRef = getDispositionScheduleImpl(nodeRef); + } + + if (diNodeRef != null) + { + di = new DispositionScheduleImpl(serviceRegistry, nodeService, diNodeRef); + } + + return di; + } + + /** + * This method returns a NodeRef + * Gets the disposition instructions + * + * @param nodeRef + * @return + */ + private NodeRef getDispositionScheduleImpl(NodeRef nodeRef) + { + NodeRef result = getAssociatedDispositionScheduleImpl(nodeRef); + + if (result == null) + { + NodeRef parent = this.nodeService.getPrimaryParent(nodeRef).getParentRef(); + if (parent != null && getRmService().isRecordCategory(parent) == true) + { + result = getDispositionScheduleImpl(parent); + } + } + return result; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#getAssociatedDispositionSchedule(org.alfresco.service.cmr.repository.NodeRef) + */ + public DispositionSchedule getAssociatedDispositionSchedule(NodeRef nodeRef) + { + DispositionSchedule ds = null; + + // Check the noderef parameter + ParameterCheck.mandatory("nodeRef", nodeRef); + if (nodeService.exists(nodeRef) == true) + { + // Get the associated disposition schedule node reference + NodeRef dsNodeRef = getAssociatedDispositionScheduleImpl(nodeRef); + if (dsNodeRef != null) + { + // Cerate disposition schedule object + ds = new DispositionScheduleImpl(serviceRegistry, nodeService, dsNodeRef); + } + } + + return ds; + } + + /** + * Gets the node reference of the disposition schedule associated with the container. + * + * @param nodeRef node reference of the container + * @return {@link NodeRef} node reference of the disposition schedule, null if none + */ + private NodeRef getAssociatedDispositionScheduleImpl(NodeRef nodeRef) + { + NodeRef result = null; + ParameterCheck.mandatory("nodeRef", nodeRef); + + // Make sure we are dealing with an RM node + if (getRmService().isFilePlanComponent(nodeRef) == false) + { + throw new AlfrescoRuntimeException("Can not find the associated disposition schedule for a non records management component. (nodeRef=" + nodeRef.toString() + ")"); + } + + if (this.nodeService.hasAspect(nodeRef, ASPECT_SCHEDULED) == true) + { + List childAssocs = this.nodeService.getChildAssocs(nodeRef, ASSOC_DISPOSITION_SCHEDULE, RegexQNamePattern.MATCH_ALL); + if (childAssocs.size() != 0) + { + ChildAssociationRef firstChildAssocRef = childAssocs.get(0); + result = firstChildAssocRef.getChildRef(); + } + } + + return result; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#getAssociatedRecordsManagementContainer(org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule) + */ + @Override + public NodeRef getAssociatedRecordsManagementContainer(DispositionSchedule dispositionSchedule) + { + ParameterCheck.mandatory("dispositionSchedule", dispositionSchedule); + NodeRef result = null; + + NodeRef dsNodeRef = dispositionSchedule.getNodeRef(); + if (nodeService.exists(dsNodeRef) == true) + { + List assocs = this.nodeService.getParentAssocs(dsNodeRef, ASSOC_DISPOSITION_SCHEDULE, RegexQNamePattern.MATCH_ALL); + if (assocs.size() != 0) + { + if (assocs.size() != 1) + { + // TODO in the future we should be able to support disposition schedule reuse, but for now just warn that + // only the first disposition schedule will be considered + if (logger.isWarnEnabled() == true) + { + logger.warn("Disposition schedule has more than one associated records management container. " + + "This is not currently supported so only the first container will be considered. " + + "(dispositionScheduleNodeRef=" + dispositionSchedule.getNodeRef().toString() + ")"); + } + } + + // Get the container reference + ChildAssociationRef assoc = assocs.get(0); + result = assoc.getParentRef(); + } + } + + return result; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#hasDisposableItems(org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule) + */ + @Override + public boolean hasDisposableItems(DispositionSchedule dispositionSchdule) + { + return !getDisposableItems(dispositionSchdule).isEmpty(); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#getDisposableItems(org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule) + */ + public List getDisposableItems(DispositionSchedule dispositionSchedule) + { + ParameterCheck.mandatory("dispositionSchedule", dispositionSchedule); + + // Get the associated container + NodeRef rmContainer = getAssociatedRecordsManagementContainer(dispositionSchedule); + + // Return the disposable items + return getDisposableItemsImpl(dispositionSchedule.isRecordLevelDisposition(), rmContainer); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#isDisposableItem(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public boolean isDisposableItem(NodeRef nodeRef) + { + return nodeService.hasAspect(nodeRef, ASPECT_DISPOSITION_LIFECYCLE); + } + + /** + * + * @param isRecordLevelDisposition + * @param rmContainer + * @param root + * @return + */ + private List getDisposableItemsImpl(boolean isRecordLevelDisposition, NodeRef rmContainer) + { + List items = getRmService().getAllContained(rmContainer); + List result = new ArrayList(items.size()); + for (NodeRef item : items) + { + if (getRmService().isRecordFolder(item) == true) + { + if (isRecordLevelDisposition == true) + { + result.addAll(getRmService().getRecords(item)); + } + else + { + result.add(item); + } + } + else if (getRmService().isRecordCategory(item) == true) + { + if (getAssociatedDispositionScheduleImpl(item) == null) + { + result.addAll(getDisposableItemsImpl(isRecordLevelDisposition, item)); + } + } + } + return result; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#createDispositionSchedule(org.alfresco.service.cmr.repository.NodeRef, java.util.Map) + */ + @Override + public DispositionSchedule createDispositionSchedule(NodeRef nodeRef, Map props) + { + NodeRef dsNodeRef = null; + + // Check mandatory parameters + ParameterCheck.mandatory("nodeRef", nodeRef); + + // Check exists + if (nodeService.exists(nodeRef) == false) + { + throw new AlfrescoRuntimeException("Unable to create disposition schedule, because node does not exist. (nodeRef=" + nodeRef.toString() + ")"); + } + + // Check is sub-type of rm:recordCategory + QName nodeRefType = nodeService.getType(nodeRef); + if (TYPE_RECORD_CATEGORY.equals(nodeRefType) == false && + dictionaryService.isSubClass(nodeRefType, TYPE_RECORD_CATEGORY) == false) + { + throw new AlfrescoRuntimeException("Unable to create disposition schedule on a node that is not a records management container."); + } + + behaviourFilter.disableBehaviour(nodeRef, ASPECT_SCHEDULED); + try + { + // Add the schedules aspect if required + if (nodeService.hasAspect(nodeRef, ASPECT_SCHEDULED) == false) + { + nodeService.addAspect(nodeRef, ASPECT_SCHEDULED, null); + } + + // Check whether there is already a disposition schedule object present + List assocs = nodeService.getChildAssocs(nodeRef, ASSOC_DISPOSITION_SCHEDULE, RegexQNamePattern.MATCH_ALL); + if (assocs.size() == 0) + { + DispositionSchedule currentDispositionSchdule = getDispositionSchedule(nodeRef); + if (currentDispositionSchdule != null) + { + List items = getDisposableItemsImpl(currentDispositionSchdule.isRecordLevelDisposition(), nodeRef); + if (items.size() != 0) + { + throw new AlfrescoRuntimeException("Can not create a disposition schedule if there are disposable items already under the control of an other disposition schedule"); + } + } + + // Create the disposition schedule object + dsNodeRef = nodeService.createNode( + nodeRef, + ASSOC_DISPOSITION_SCHEDULE, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, QName.createValidLocalName("dispositionSchedule")), + TYPE_DISPOSITION_SCHEDULE, + props).getChildRef(); + } + else + { + // Error since the node already has a disposition schedule set + throw new AlfrescoRuntimeException("Unable to create disposition schedule on node that already has a disposition schedule."); + } + } + finally + { + behaviourFilter.enableBehaviour(nodeRef, ASPECT_SCHEDULED); + } + + // Create the return object + return new DispositionScheduleImpl(serviceRegistry, nodeService, dsNodeRef); + } + + /** ========= Disposition Action Definition Methods ========= */ + + /** + * + */ + public DispositionActionDefinition addDispositionActionDefinition( + DispositionSchedule schedule, + Map actionDefinitionParams) + { + // make sure at least a name has been defined + String name = (String)actionDefinitionParams.get(PROP_DISPOSITION_ACTION_NAME); + if (name == null || name.length() == 0) + { + throw new IllegalArgumentException("'name' parameter is mandatory when creating a disposition action definition"); + } + + // TODO: also check the action name is valid? + + // create the child association from the schedule to the action definition + NodeRef actionNodeRef = this.nodeService.createNode(schedule.getNodeRef(), + RecordsManagementModel.ASSOC_DISPOSITION_ACTION_DEFINITIONS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, + QName.createValidLocalName(name)), + RecordsManagementModel.TYPE_DISPOSITION_ACTION_DEFINITION, actionDefinitionParams).getChildRef(); + + // get the updated disposition schedule and retrieve the new action definition + NodeRef scheduleParent = this.nodeService.getPrimaryParent(schedule.getNodeRef()).getParentRef(); + DispositionSchedule updatedSchedule = this.getDispositionSchedule(scheduleParent); + return updatedSchedule.getDispositionActionDefinition(actionNodeRef.getId()); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#removeDispositionActionDefinition(org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule, org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition) + */ + public void removeDispositionActionDefinition(DispositionSchedule schedule, DispositionActionDefinition actionDefinition) + { + // check first whether action definitions can be removed + if (hasDisposableItems(schedule) == true) + { + throw new AlfrescoRuntimeException("Can not remove action definitions from schedule '" + + schedule.getNodeRef() + "' as one or more record or record folders are present."); + } + + // remove the child node representing the action definition + this.nodeService.removeChild(schedule.getNodeRef(), actionDefinition.getNodeRef()); + } + + /** + * Updates the given disposition action definition belonging to the given disposition + * schedule. + * + * @param schedule The DispositionSchedule the action belongs to + * @param actionDefinition The DispositionActionDefinition to update + * @param actionDefinitionParams Map of parameters to use to update the action definition + * @return The updated DispositionActionDefinition + */ + public DispositionActionDefinition updateDispositionActionDefinition( + DispositionActionDefinition actionDefinition, + Map actionDefinitionParams) + { + // update the node with properties + this.nodeService.addProperties(actionDefinition.getNodeRef(), actionDefinitionParams); + + // get the updated disposition schedule and retrieve the updated action definition + NodeRef ds = this.nodeService.getPrimaryParent(actionDefinition.getNodeRef()).getParentRef(); + DispositionSchedule updatedSchedule = new DispositionScheduleImpl(serviceRegistry, nodeService, ds); + return updatedSchedule.getDispositionActionDefinition(actionDefinition.getId()); + } + + /** + * + */ + public boolean isNextDispositionActionEligible(NodeRef nodeRef) + { + boolean result = false; + + // Get the disposition instructions + DispositionSchedule di = getDispositionSchedule(nodeRef); + NodeRef nextDa = getNextDispositionActionNodeRef(nodeRef); + if (di != null && + this.nodeService.hasAspect(nodeRef, ASPECT_DISPOSITION_LIFECYCLE) == true && + nextDa != null) + { + // If it has an asOf date and it is greater than now the action is eligible + Date asOf = (Date)this.nodeService.getProperty(nextDa, PROP_DISPOSITION_AS_OF); + if (asOf != null && + asOf.before(new Date()) == true) + { + result = true; + } + + if (result == false) + { + // If all the events specified on the action have been completed the action is eligible + List assocs = this.nodeService.getChildAssocs(nextDa, ASSOC_EVENT_EXECUTIONS, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef assoc : assocs) + { + NodeRef eventExecution = assoc.getChildRef(); + Boolean isCompleteValue = (Boolean)this.nodeService.getProperty(eventExecution, PROP_EVENT_EXECUTION_COMPLETE); + boolean isComplete = false; + if (isCompleteValue != null) + { + isComplete = isCompleteValue.booleanValue(); + + // TODO this only works for the OR use case .. need to handle optional AND handling + if (isComplete == true) + { + result = true; + break; + } + } + } + } + } + + return result; + } + + /** + * Get the next disposition action node. Null if none present. + * + * @param nodeRef the disposable node reference + * @return NodeRef the next disposition action, null if none + */ + private NodeRef getNextDispositionActionNodeRef(NodeRef nodeRef) + { + NodeRef result = null; + List assocs = this.nodeService.getChildAssocs(nodeRef, ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL); + if (assocs.size() != 0) + { + result = assocs.get(0).getChildRef(); + } + return result; + } + + /** ========= Disposition Action Methods ========= */ + + /** + * + */ + public DispositionAction getNextDispositionAction(NodeRef nodeRef) + { + DispositionAction result = null; + NodeRef dispositionActionNodeRef = getNextDispositionActionNodeRef(nodeRef); + + if (dispositionActionNodeRef != null) + { + result = new DispositionActionImpl(this.serviceRegistry, dispositionActionNodeRef); + } + return result; + } + + + /** ========= Disposition Action History Methods ========= */ + + public List getCompletedDispositionActions(NodeRef nodeRef) + { + List assocs = this.nodeService.getChildAssocs(nodeRef, ASSOC_DISPOSITION_ACTION_HISTORY, RegexQNamePattern.MATCH_ALL); + List result = new ArrayList(assocs.size()); + for (ChildAssociationRef assoc : assocs) + { + NodeRef dispositionActionNodeRef = assoc.getChildRef(); + result.add(new DispositionActionImpl(serviceRegistry, dispositionActionNodeRef)); + } + return result; + } + + public DispositionAction getLastCompletedDispostionAction(NodeRef nodeRef) + { + DispositionAction result = null; + List list = getCompletedDispositionActions(nodeRef); + if (list.isEmpty() == false) + { + // Get the last disposition action in the list + result = list.get(list.size()-1); + } + return result; + } + + + public List getDispositionPeriodProperties() + { + DispositionPeriodProperties dpp = (DispositionPeriodProperties)applicationContext.getBean(DispositionPeriodProperties.BEAN_NAME); + + if (dpp == null) + { + return Collections.emptyList(); + } + else + { + return dpp.getPeriodProperties(); + } + } + + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/dod5015/DOD5015Model.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/dod5015/DOD5015Model.java new file mode 100644 index 0000000000..3b9420ca1c --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/dod5015/DOD5015Model.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.dod5015; + +import org.alfresco.service.namespace.QName; + + +/** + * Helper class containing DOD 5015 model qualified names + * + * @author Roy Wetherall + */ +public interface DOD5015Model +{ + // Namespace details + public static final String DOD_URI = "http://www.alfresco.org/model/dod5015/1.0"; + public static final String DOD_PREFIX = "dod"; + + // Record series DOD type + public static final QName TYPE_RECORD_SERIES = QName.createQName(DOD_URI, "recordSeries"); + + // DOD 5015 Custom Type aspects and their properties + // Scanned Record + public static final QName ASPECT_SCANNED_RECORD = QName.createQName(DOD_URI, "scannedRecord"); + public static final QName PROP_SCANNED_FORMAT = QName.createQName(DOD_URI, "scannedFormat"); + public static final QName PROP_SCANNED_FORMAT_VERSION = QName.createQName(DOD_URI, "scannedFormatVersion"); + public static final QName PROP_RESOLUTION_X = QName.createQName(DOD_URI, "resolutionX"); + public static final QName PROP_RESOLUTION_Y = QName.createQName(DOD_URI, "resolutionY"); + public static final QName PROP_SCANNED_BIT_DEPTH = QName.createQName(DOD_URI, "scannedBitDepth"); + + // PDF Record + public static final QName ASPECT_PDF_RECORD = QName.createQName(DOD_URI, "pdfRecord"); + public static final QName PROP_PRODUCING_APPLICATION = QName.createQName(DOD_URI, "producingApplication"); + public static final QName PROP_PRODUCING_APPLICATION_VERSION = QName.createQName(DOD_URI, "producingApplicationVersion"); + public static final QName PROP_PDF_VERSION = QName.createQName(DOD_URI, "pdfVersion"); + public static final QName PROP_CREATING_APPLICATION = QName.createQName(DOD_URI, "creatingApplication"); + public static final QName PROP_DOCUMENT_SECURITY_SETTINGS = QName.createQName(DOD_URI, "documentSecuritySettings"); + + // Digital Photograph Record + public static final QName ASPECT_DIGITAL_PHOTOGRAPH_RECORD = QName.createQName(DOD_URI, "digitalPhotographRecord"); + public static final QName PROP_CAPTION = QName.createQName(DOD_URI, "caption"); + public static final QName PROP_PHOTOGRAPHER = QName.createQName(DOD_URI, "photographer"); + public static final QName PROP_COPYRIGHT = QName.createQName(DOD_URI, "copyright"); + public static final QName PROP_BIT_DEPTH = QName.createQName(DOD_URI, "bitDepth"); + public static final QName PROP_IMAGE_SIZE_X = QName.createQName(DOD_URI, "imageSizeX"); + public static final QName PROP_IMAGE_SIZE_Y = QName.createQName(DOD_URI, "imageSizeY"); + public static final QName PROP_IMAGE_SOURCE = QName.createQName(DOD_URI, "imageSource"); + public static final QName PROP_COMPRESSION = QName.createQName(DOD_URI, "compression"); + public static final QName PROP_ICC_ICM_PROFILE = QName.createQName(DOD_URI, "iccIcmProfile"); + public static final QName PROP_EXIF_INFORMATION = QName.createQName(DOD_URI, "exifInformation"); + + // Web Record + public static final QName ASPECT_WEB_RECORD = QName.createQName(DOD_URI, "webRecord"); + public static final QName PROP_WEB_FILE_NAME = QName.createQName(DOD_URI, "webFileName"); + public static final QName PROP_WEB_PLATFORM = QName.createQName(DOD_URI, "webPlatform"); + public static final QName PROP_WEBSITE_NAME = QName.createQName(DOD_URI, "webSiteName"); + public static final QName PROP_WEB_SITE_URL = QName.createQName(DOD_URI, "webSiteURL"); + public static final QName PROP_CAPTURE_METHOD = QName.createQName(DOD_URI, "captureMethod"); + public static final QName PROP_CAPTURE_DATE = QName.createQName(DOD_URI, "captureDate"); + public static final QName PROP_CONTACT = QName.createQName(DOD_URI, "contact"); + public static final QName PROP_CONTENT_MANAGEMENT_SYSTEM = QName.createQName(DOD_URI, "contentManagementSystem"); +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/email/CustomEmailMappingService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/email/CustomEmailMappingService.java new file mode 100644 index 0000000000..a64df4f860 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/email/CustomEmailMappingService.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.email; + +import java.util.Set; + +public interface CustomEmailMappingService +{ + public Set getCustomMappings(); + + public void addCustomMapping(String from, String to); + + public void deleteCustomMapping(String from, String to); + + public void init(); +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/email/CustomEmailMappingServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/email/CustomEmailMappingServiceImpl.java new file mode 100644 index 0000000000..9b4be07048 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/email/CustomEmailMappingServiceImpl.java @@ -0,0 +1,411 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.email; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.content.ContentServicePolicies; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.content.metadata.RFC822MetadataExtracter; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; + +public class CustomEmailMappingServiceImpl implements CustomEmailMappingService +{ + + private RFC822MetadataExtracter extracter; + private NodeService nodeService; + private NamespacePrefixResolver nspr; + private PolicyComponent policyComponent; + private ContentService contentService; + private TransactionService transactionService; + + private Set customMappings = Collections.synchronizedSet(new HashSet()); + + private static Log logger = LogFactory.getLog(CustomEmailMappingServiceImpl.class); + + + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * Get the name space prefix resolver + * @return the name space prefix resolver + */ + public NamespacePrefixResolver getNamespacePrefixResolver() + { + return nspr; + } + + /** + * Set the name space prefix resolver + * @param nspr + */ + public void setNamespacePrefixResolver(NamespacePrefixResolver nspr) + { + this.nspr = nspr; + } + + /** + * + */ + public void init() + { + CustomMapping[] rmHardCodedMappings = { + new CustomMapping("Date", "rma:dateReceived"), + new CustomMapping("messageTo", "rma:address"), + new CustomMapping("messageFrom", "rma:originator"), + new CustomMapping("messageSent", "rma:publicationDate"), + new CustomMapping("messageCc", "rma:otherAddress") + }; + + NodeRef configNode = getConfigNode(); + if(configNode != null) + { + /** + * Get any custom mappings. + */ + customMappings = readConfig(configNode); + } + + /** + * ensure that the customMappings contain the RM specific mappings + */ + for(CustomMapping mapping : rmHardCodedMappings) + { + if(!customMappings.contains(mapping)) + { + customMappings.add(mapping); + } + } + + // Get the read only existing configuration + Map> currentMapping = extracter.getCurrentMapping(); + + Map> newMapping = new HashMap>(17); + newMapping.putAll(currentMapping); + + for(CustomMapping mapping : customMappings) + { + QName newQName = QName.createQName(mapping.getTo(), nspr); + Set values = newMapping.get(mapping.getFrom()); + if(values == null) + { + values = new HashSet(); + newMapping.put(mapping.getFrom(), values); + } + values.add(newQName); + } + + // Now update the metadata extracter + extracter.setMapping(newMapping); + + // Register interest in the onContentUpdate policy + policyComponent.bindClassBehaviour( + ContentServicePolicies.OnContentUpdatePolicy.QNAME, + RecordsManagementModel.TYPE_EMAIL_CONFIG, + new JavaBehaviour(this, "onContentUpdate")); + + } + + public void onContentUpdate(NodeRef nodeRef, boolean newContent) + { + NodeRef configNode = getConfigNode(); + if(configNode != null) + { + Set newMappings = readConfig(configNode); + + customMappings.addAll(newMappings); + + for(CustomMapping mapping : customMappings) + { + if(!newMappings.contains(mapping)) + { + customMappings.remove(mapping); + } + } + } + } + + public void beforeDeleteNode(NodeRef nodeRef) + { + } + + public void onCreateNode(ChildAssociationRef childAssocRef) + { + + } + + public Set getCustomMappings() + { + // add all the lists data to a Map + Set emailMap = new HashSet(); + + Map> currentMapping = extracter.getCurrentMapping(); + + for(String key : currentMapping.keySet()) + { + Set set = currentMapping.get(key); + + for(QName qname : set) + { + CustomMapping value = new CustomMapping(); + value.setFrom(key); + QName resolvedQname = qname.getPrefixedQName(nspr); + value.setTo(resolvedQname.toPrefixString()); + emailMap.add(value); + } + } + + return emailMap; + } + + + public void addCustomMapping(String from, String to) + { + // Get the read only existing configuration + Map> currentMapping = extracter.getCurrentMapping(); + + Map> newMapping = new HashMap>(17); + newMapping.putAll(currentMapping); + + QName newQName = QName.createQName(to, nspr); + + Set values = newMapping.get(from); + if(values == null) + { + values = new HashSet(); + newMapping.put(from, values); + } + values.add(newQName); + + CustomMapping xxx = new CustomMapping(); + xxx.setFrom(from); + xxx.setTo(to); + customMappings.add(xxx); + + updateOrCreateEmailConfig(customMappings); + + // Crash in the new config. + extracter.setMapping(newMapping); + } + + public void deleteCustomMapping(String from, String to) + { + // Get the read only existing configuration + Map> currentMapping = extracter.getCurrentMapping(); + + Map> newMapping = new HashMap>(17); + newMapping.putAll(currentMapping); + + QName oldQName = QName.createQName(to, nspr); + + Set values = newMapping.get(from); + if(values != null) + { + values.remove(oldQName); + } + + CustomMapping toDelete = new CustomMapping(from, to); + customMappings.remove(toDelete); + + updateOrCreateEmailConfig(customMappings); + + // Crash in the new config. + extracter.setMapping(newMapping); + } + + public void setExtracter(RFC822MetadataExtracter extractor) + { + this.extracter = extractor; + } + + public RFC822MetadataExtracter getExtracter() + { + return extracter; + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public NodeService getNodeService() + { + return nodeService; + } + + // Default + private StoreRef storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore"); + + private static final String CONFIG_NAME = "imapConfig.json"; + + /** + * + * @param nodeRef + * @return + */ + private Set readConfig(NodeRef nodeRef) + { + Set newMappings = new HashSet(); + + ContentReader cr = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT); + if (cr != null) + { + String text = cr.getContentString(); + + try + { + JSONArray jsonArray = new JSONArray(new JSONTokener(text)); + for(int i = 0 ; i < jsonArray.length(); i++) + { + JSONObject obj = jsonArray.getJSONObject(i); + CustomMapping mapping = new CustomMapping(); + mapping.setFrom(obj.getString("from")); + mapping.setTo(obj.getString("to")); + newMappings.add(mapping); + } + return newMappings; + } + catch (JSONException je) + { + logger.warn("unable to read custom email configuration", je); + return newMappings; + } + + } + return newMappings; + } + + public NodeRef updateOrCreateEmailConfig(Set customMappings) + { + NodeRef caveatConfig = updateOrCreateEmailConfig(); + + try + { + JSONArray mappings = new JSONArray(); + for(CustomMapping mapping : customMappings) + { + JSONObject obj = new JSONObject(); + obj.put("from", mapping.getFrom()); + obj.put("to", mapping.getTo()); + mappings.put(obj); + } + + // Update the content + ContentWriter writer = this.contentService.getWriter(caveatConfig, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent(mappings.toString()); + } + catch (JSONException je) + { + + } + + + return caveatConfig; + } + + public NodeRef updateOrCreateEmailConfig(String txt) + { + NodeRef caveatConfig = updateOrCreateEmailConfig(); + + // Update the content + ContentWriter writer = this.contentService.getWriter(caveatConfig, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent(txt); + + return caveatConfig; + } + + private NodeRef updateOrCreateEmailConfig() + { + NodeRef caveatConfig = getConfigNode(); + if (caveatConfig == null) + { + logger.debug("custom email configuration does not exist - creating new"); + NodeRef rootNode = nodeService.getRootNode(storeRef); + //nodeService.addAspect(rootNode, VersionModel.ASPECT_VERSION_STORE_ROOT, null); + + // Create caveat config + caveatConfig = nodeService.createNode(rootNode, + RecordsManagementModel.ASSOC_EMAIL_CONFIG, + QName.createQName(RecordsManagementModel.RM_URI, CONFIG_NAME), + RecordsManagementModel.TYPE_EMAIL_CONFIG).getChildRef(); + + nodeService.setProperty(caveatConfig, ContentModel.PROP_NAME, CONFIG_NAME); + } + + return caveatConfig; + } + + public NodeRef getConfigNode() + { + NodeRef rootNode = nodeService.getRootNode(storeRef); + return nodeService.getChildByName(rootNode, RecordsManagementModel.ASSOC_EMAIL_CONFIG, CONFIG_NAME); + } + + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + + public ContentService getContentService() + { + return contentService; + } + + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } + + public TransactionService getTransactionService() + { + return transactionService; + } + + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/email/CustomMapping.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/email/CustomMapping.java new file mode 100644 index 0000000000..b9f75d1532 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/email/CustomMapping.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.email; + + +public class CustomMapping +{ + private String from; + private String to; + + public CustomMapping() + { + + } + + public CustomMapping(String from, String to) + { + this.from = from; + this.to = to; + } + + public void setFrom(String from) + { + this.from = from; + } + + public String getFrom() + { + return from; + } + + public void setTo(String to) + { + this.to = to; + } + + public String getTo() + { + return to; + } + + public int hashCode() + { + if(from != null && to != null) + { + return (from + to).hashCode(); + } + else + { + return 1; + } + } + + /* + * (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + + final CustomMapping other = (CustomMapping) obj; + + if (!from.equals(other.getFrom())) + { + return false; + } + if (!to.equals(other.getTo())) + { + return false; + } + return true; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/email/RFC822MetadataExtracter.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/email/RFC822MetadataExtracter.java new file mode 100644 index 0000000000..65f28e6919 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/email/RFC822MetadataExtracter.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.email; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.QName; + +/** + * Extended RFC822 Metadata Extractor that is sensitive to whether we are in a RM + * site or not. + * + * @author Roy Wetherall + */ +public class RFC822MetadataExtracter extends org.alfresco.repo.content.metadata.RFC822MetadataExtracter +{ + /** Reference to default properties */ + private static final String PROPERTIES_URL = "org/alfresco/repo/content/metadata/RFC822MetadataExtracter.properties"; + + /** Node service */ + private NodeService nodeService; + + /** + * Sets the node service + * @param nodeService node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @see org.alfresco.repo.content.metadata.AbstractMappingMetadataExtracter#filterSystemProperties(java.util.Map, java.util.Map) + */ + @Override + protected void filterSystemProperties(Map systemProperties, Map targetProperties) + { + NodeRef nodeRef = getNodeRef(targetProperties); + if (nodeRef == null || nodeService.hasAspect(nodeRef, RecordsManagementModel.ASPECT_RECORD) == false) + { + // Remove all rm namespace properties from the system map + Map clone = new HashMap(systemProperties); + for (QName propName : clone.keySet()) + { + if (RecordsManagementModel.RM_URI.equals(propName.getNamespaceURI()) == true) + { + systemProperties.remove(propName); + } + } + } + } + + /** + * @see org.alfresco.repo.content.metadata.AbstractMappingMetadataExtracter#getDefaultMapping() + */ + protected Map> getDefaultMapping() + { + // Attempt to load the properties + return readMappingProperties(PROPERTIES_URL); + } + + /** + * Given a set of properties, try and retrieve the node reference + * @param properties node properties + * @return NodeRef null if none, otherwise valid node reference + */ + private NodeRef getNodeRef(Map properties) + { + NodeRef result = null; + + // Get the elements of the node reference + String storeProto = (String)properties.get(ContentModel.PROP_STORE_PROTOCOL); + String storeId = (String)properties.get(ContentModel.PROP_STORE_IDENTIFIER); + String nodeId = (String)properties.get(ContentModel.PROP_NODE_UUID); + + if (storeProto != null && storeProto.length() != 0 && + storeId != null && storeId.length() != 0 && + nodeId != null && nodeId.length() != 0) + + { + // Create the node reference + result = new NodeRef(new StoreRef(storeProto, storeId), nodeId); + } + + return result; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/event/EventCompletionDetails.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/event/EventCompletionDetails.java new file mode 100644 index 0000000000..6641fb1c31 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/event/EventCompletionDetails.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.event; + +import java.util.Date; + +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Event completion details + * + * @author Roy Wetherall + */ +public class EventCompletionDetails +{ + private NodeRef nodeRef; + private String eventName; + private String eventLabel; + private boolean eventExecutionAutomatic; + private boolean eventComplete; + private Date eventCompletedAt; + private String eventCompletedBy; + + + /** + * @param nodeRef + * @param eventName + * @param eventLabel + * @param eventExecutionAutomatic + * @param eventComplete + * @param eventCompletedAt + * @param eventCompletedBy + */ + public EventCompletionDetails( NodeRef nodeRef, + String eventName, + String eventLabel, + boolean eventExecutionAutomatic, + boolean eventComplete, + Date eventCompletedAt, + String eventCompletedBy) + { + this.nodeRef = nodeRef; + this.eventName = eventName; + this.eventLabel = eventLabel; + this.eventExecutionAutomatic = eventExecutionAutomatic; + this.eventComplete = eventComplete; + this.eventCompletedAt = eventCompletedAt; + this.eventCompletedBy = eventCompletedBy; + } + + /** + * @return the node reference + */ + public NodeRef getNodeRef() + { + return nodeRef; + } + + /** + * @return the eventName + */ + public String getEventName() + { + return eventName; + } + + /** + * @param eventName the eventName to set + */ + public void setEventName(String eventName) + { + this.eventName = eventName; + } + + /** + * @return The display label of the event + */ + public String getEventLabel() + { + return this.eventLabel; + } + + /** + * @return the eventExecutionAutomatic + */ + public boolean isEventExecutionAutomatic() + { + return eventExecutionAutomatic; + } + + /** + * @param eventExecutionAutomatic the eventExecutionAutomatic to set + */ + public void setEventExecutionAutomatic(boolean eventExecutionAutomatic) + { + this.eventExecutionAutomatic = eventExecutionAutomatic; + } + + /** + * @return the eventComplete + */ + public boolean isEventComplete() + { + return eventComplete; + } + + /** + * @param eventComplete the eventComplete to set + */ + public void setEventComplete(boolean eventComplete) + { + this.eventComplete = eventComplete; + } + + /** + * @return the eventCompletedAt + */ + public Date getEventCompletedAt() + { + return eventCompletedAt; + } + + /** + * @param eventCompletedAt the eventCompletedAt to set + */ + public void setEventCompletedAt(Date eventCompletedAt) + { + this.eventCompletedAt = eventCompletedAt; + } + + /** + * @return the eventCompletedBy + */ + public String getEventCompletedBy() + { + return eventCompletedBy; + } + + /** + * @param eventCompletedBy the eventCompletedBy to set + */ + public void setEventCompletedBy(String eventCompletedBy) + { + this.eventCompletedBy = eventCompletedBy; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/event/OnReferenceCreateEventType.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/event/OnReferenceCreateEventType.java new file mode 100644 index 0000000000..58f37ef1a1 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/event/OnReferenceCreateEventType.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.event; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.OnCreateReference; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; +import org.alfresco.module.org_alfresco_module_rm.action.impl.CompleteEventAction; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.policy.Behaviour.NotificationFrequency; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * On reference create event type + * + * @author Roy Wetherall + */ +public class OnReferenceCreateEventType extends SimpleRecordsManagementEventTypeImpl + implements RecordsManagementModel, + OnCreateReference +{ + /** Records management service */ + @SuppressWarnings("unused") + private RecordsManagementService recordsManagementService; + + /** Records management action service */ + private RecordsManagementActionService recordsManagementActionService; + + /** Disposition service */ + private DispositionService dispositionService; + + /** Policy component */ + private PolicyComponent policyComponent; + + /** Reference */ + private QName reference; + + /** + * @param recordsManagementService the records management service to set + */ + public void setRecordsManagementService(RecordsManagementService recordsManagementService) + { + this.recordsManagementService = recordsManagementService; + } + + /** + * @param dispositionService the disposition service to set + */ + public void setDispositionService(DispositionService dispositionService) + { + this.dispositionService = dispositionService; + } + + /** + * @param recordsManagementActionService the recordsManagementActionService to set + */ + public void setRecordsManagementActionService(RecordsManagementActionService recordsManagementActionService) + { + this.recordsManagementActionService = recordsManagementActionService; + } + + /** + * Set policy components + * + * @param policyComponent policy component + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * Set the reference + * + * @param reference + */ + public void setReferenceName(String reference) + { + this.reference = QName.createQName(reference); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.event.SimpleRecordsManagementEventTypeImpl#init() + */ + public void init() + { + super.init(); + + // Register interest in the on create reference policy + policyComponent.bindClassBehaviour(RecordsManagementPolicies.ON_CREATE_REFERENCE, + ASPECT_RECORD, + new JavaBehaviour(this, "onCreateReference", NotificationFrequency.TRANSACTION_COMMIT)); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.event.SimpleRecordsManagementEventTypeImpl#isAutomaticEvent() + */ + @Override + public boolean isAutomaticEvent() + { + return true; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.OnCreateReference#onCreateReference(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void onCreateReference(final NodeRef fromNodeRef, final NodeRef toNodeRef, final QName reference) + { + AuthenticationUtil.RunAsWork work = new AuthenticationUtil.RunAsWork() + { + public Object doWork() throws Exception + { + // Check whether it is the reference type we care about + if (reference.equals(OnReferenceCreateEventType.this.reference) == true) + { + DispositionAction da = dispositionService.getNextDispositionAction(toNodeRef); + if (da != null) + { + List events = da.getEventCompletionDetails(); + for (EventCompletionDetails event : events) + { + RecordsManagementEvent rmEvent = recordsManagementEventService.getEvent(event.getEventName()); + if (event.isEventComplete() == false && + rmEvent.getType().equals(getName()) == true) + { + // Complete the event + Map params = new HashMap(3); + params.put(CompleteEventAction.PARAM_EVENT_NAME, event.getEventName()); + params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_BY, AuthenticationUtil.getFullyAuthenticatedUser()); + params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_AT, new Date()); + recordsManagementActionService.executeRecordsManagementAction(toNodeRef, "completeEvent", params); + + break; + } + } + } + } + + return null; + } + }; + + AuthenticationUtil.runAs(work, AuthenticationUtil.getAdminUserName()); + + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/event/OnReferencedRecordActionedUpon.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/event/OnReferencedRecordActionedUpon.java new file mode 100644 index 0000000000..ad8dc7558a --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/event/OnReferencedRecordActionedUpon.java @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.event; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; +import org.alfresco.module.org_alfresco_module_rm.action.impl.CompleteEventAction; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.policy.Behaviour.NotificationFrequency; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; + +/** + * + * + * @author Roy Wetherall + */ +public class OnReferencedRecordActionedUpon extends SimpleRecordsManagementEventTypeImpl + implements RecordsManagementModel + +{ + /** Records management service */ + private RecordsManagementService recordsManagementService; + + /** Disposition service */ + private DispositionService dispositionService; + + /** Records management action service */ + private RecordsManagementActionService recordsManagementActionService; + + /** Records management admin service */ + private RecordsManagementAdminService recordsManagementAdminService; + + /** Node service */ + private NodeService nodeService; + + /** Policy component */ + private PolicyComponent policyComponent; + + /** Action name */ + private String actionName; + + /** Reference */ + private QName reference; + + /** + * @param recordsManagementService the records management service to set + */ + public void setRecordsManagementService(RecordsManagementService recordsManagementService) + { + this.recordsManagementService = recordsManagementService; + } + + /** + * @param dispositionService the disposition service + */ + public void setDispositionService(DispositionService dispositionService) + { + this.dispositionService = dispositionService; + } + + /** + * @param recordsManagementActionService the recordsManagementActionService to set + */ + public void setRecordsManagementActionService(RecordsManagementActionService recordsManagementActionService) + { + this.recordsManagementActionService = recordsManagementActionService; + } + + public void setRecordsManagementAdminService(RecordsManagementAdminService recordsManagementAdminService) + { + this.recordsManagementAdminService = recordsManagementAdminService; + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Set policy components + * + * @param policyComponent policy component + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * Set the reference + * + * @param reference + */ + public void setReferenceName(String reference) + { + this.reference = QName.createQName(reference); + } + + public void setActionName(String actionName) + { + this.actionName = actionName; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.event.SimpleRecordsManagementEventTypeImpl#init() + */ + public void init() + { + super.init(); + + // Register interest in the on create reference policy + policyComponent.bindClassBehaviour(RecordsManagementPolicies.BEFORE_RM_ACTION_EXECUTION, + ASPECT_FILE_PLAN_COMPONENT, + new JavaBehaviour(this, "beforeActionExecution", NotificationFrequency.FIRST_EVENT)); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.event.SimpleRecordsManagementEventTypeImpl#isAutomaticEvent() + */ + @Override + public boolean isAutomaticEvent() + { + return true; + } + + public void beforeActionExecution(final NodeRef nodeRef, final String name, final Map parameters) + { + AuthenticationUtil.RunAsWork work = new AuthenticationUtil.RunAsWork() + { + public Object doWork() throws Exception + { + if (nodeService.exists(nodeRef) == true) + { + if (name.equals(actionName) == true) + { + QName type = nodeService.getType(nodeRef); + if (TYPE_TRANSFER.equals(type) == true) + { + List assocs = nodeService.getChildAssocs(nodeRef, ASSOC_TRANSFERRED, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef assoc : assocs) + { + processRecordFolder(assoc.getChildRef()); + } + } + else + { + processRecordFolder(nodeRef); + } + } + } + + return null; + } + }; + + AuthenticationUtil.runAs(work, AuthenticationUtil.getAdminUserName()); + + } + + private void processRecordFolder(NodeRef recordFolder) + { + if (recordsManagementService.isRecord(recordFolder) == true) + { + processRecord(recordFolder); + } + else if (recordsManagementService.isRecordFolder(recordFolder) == true) + { + for (NodeRef record : recordsManagementService.getRecords(recordFolder)) + { + processRecord(record); + } + } + } + + private void processRecord(NodeRef record) + { + List fromAssocs = recordsManagementAdminService.getCustomReferencesFrom(record); + for (AssociationRef fromAssoc : fromAssocs) + { + if (reference.equals(fromAssoc.getTypeQName()) == true) + { + NodeRef nodeRef = fromAssoc.getTargetRef(); + doEventComplete(nodeRef); + } + } + + List toAssocs = recordsManagementAdminService.getCustomReferencesTo(record); + for (AssociationRef toAssoc : toAssocs) + { + if (reference.equals(toAssoc.getTypeQName()) == true) + { + NodeRef nodeRef = toAssoc.getSourceRef(); + doEventComplete(nodeRef); + } + } + } + + private void doEventComplete(NodeRef nodeRef) + { + DispositionAction da = dispositionService.getNextDispositionAction(nodeRef); + if (da != null) + { + List events = da.getEventCompletionDetails(); + for (EventCompletionDetails event : events) + { + RecordsManagementEvent rmEvent = recordsManagementEventService.getEvent(event.getEventName()); + if (event.isEventComplete() == false && + rmEvent.getType().equals(getName()) == true) + { + // Complete the event + Map params = new HashMap(3); + params.put(CompleteEventAction.PARAM_EVENT_NAME, event.getEventName()); + params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_BY, AuthenticationUtil.getFullyAuthenticatedUser()); + params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_AT, new Date()); + recordsManagementActionService.executeRecordsManagementAction(nodeRef, "completeEvent", params); + + break; + } + } + } + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/event/RecordsManagementEvent.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/event/RecordsManagementEvent.java new file mode 100644 index 0000000000..da6e95e3a8 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/event/RecordsManagementEvent.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.event; + +/** + * Records management event + * + * @author Roy Wetherall + */ +public class RecordsManagementEvent +{ + /** Records management event type */ + private String type; + + /** Records management event name */ + private String name; + + /** Records management display label */ + private String displayLabel; + + /** + * Constructor + * + * @param type event type + * @param name event name + * @param displayLabel event display label + */ + public RecordsManagementEvent(String type, String name, String displayLabel) + { + this.type = type; + this.name = name; + this.displayLabel = displayLabel; + } + + /** + * Get records management type + * + * @return String records management type + */ + public String getType() + { + return this.type; + } + + /** + * Event name + * + * @return String event name + */ + public String getName() + { + return this.name; + } + + /** + * + * @return + */ + public String getDisplayLabel() + { + return displayLabel; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/event/RecordsManagementEventService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/event/RecordsManagementEventService.java new file mode 100644 index 0000000000..5e60e5b64e --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/event/RecordsManagementEventService.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.event; + +import java.util.List; + +/** + * Records management event service interface + * + * @author Roy Wetherall + */ +public interface RecordsManagementEventService +{ + /** + * Register an event type + * + * @param eventType event type + */ + void registerEventType(RecordsManagementEventType eventType); + + /** + * Get a list of the event types + * + * @return List list of the event types + */ + List getEventTypes(); + + /** + * Get the records management event type + * + * @param eventType name + * @return RecordsManagementEventType event type + */ + RecordsManagementEventType getEventType(String eventTypeName); + + /** + * Get the list of available events + * + * @return List list of events + */ + List getEvents(); + + /** + * Get a records management event given its name. Returns null if the event name is not + * recognised. + * + * @param eventName event name + * @return RecordsManagementEvent event + */ + RecordsManagementEvent getEvent(String eventName); + + /** + * Indicates whether a perticular event exists. Returns true if it does, false otherwise. + * + * @param eventName event name + * @return boolean true if event exists, false otherwise + */ + boolean existsEvent(String eventName); + + /** + * Add an event + * + * @param eventType event type + * @param eventName event name + * @param eventDisplayLabel event display label + */ + RecordsManagementEvent addEvent(String eventType, String eventName, String eventDisplayLabel); + + /** + * Remove an event + * + * @param eventName event name + */ + void removeEvent(String eventName); + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/event/RecordsManagementEventServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/event/RecordsManagementEventServiceImpl.java new file mode 100644 index 0000000000..b5084747d7 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/event/RecordsManagementEventServiceImpl.java @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.event; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.json.JSONArray; +import org.json.JSONObject; + +/** + * Records management event service implementation + * + * @author Roy Wetherall + */ +public class RecordsManagementEventServiceImpl implements RecordsManagementEventService +{ + /** Reference to the rm event config node */ + private static final StoreRef SPACES_STORE = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore"); + private static final NodeRef CONFIG_NODE_REF = new NodeRef(SPACES_STORE, "rm_event_config"); + + /** Node service */ + private NodeService nodeService; + + /** Content service */ + private ContentService contentService; + + /** Registered event types */ + private Map eventTypes = new HashMap(7); + + /** Available events */ + private Map events; + + /** + * Set the node service + * + * @param nodeService node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Set the content service + * + * @param contentService content service + */ + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService#registerEventType(org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventType) + */ + public void registerEventType(RecordsManagementEventType eventType) + { + this.eventTypes.put(eventType.getName(), eventType); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService#getEventTypes() + */ + public List getEventTypes() + { + return new ArrayList(this.eventTypes.values()); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService#getEvents() + */ + public List getEvents() + { + return new ArrayList(this.getEventMap().values()); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService#getEvent(java.lang.String) + */ + public RecordsManagementEvent getEvent(String eventName) + { + if (getEventMap().containsKey(eventName) == false) + { + throw new AlfrescoRuntimeException("The event " + eventName + " does not exist."); + } + return getEventMap().get(eventName); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService#existsEvent(java.lang.String) + */ + public boolean existsEvent(String eventName) + { + return getEventMap().containsKey(eventName); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService#addEvent(java.lang.String, java.lang.String, java.lang.String) + */ + public RecordsManagementEvent addEvent(String eventType, String eventName, String eventDisplayLabel) + { + // Check that the eventType is valid + if (eventTypes.containsKey(eventType) == false) + { + throw new AlfrescoRuntimeException( + "Can not add event because event " + + eventName + + " has an undefined eventType. (" + + eventType + ")"); + } + + // Create event and add to map + RecordsManagementEvent event = new RecordsManagementEvent(eventType, eventName, eventDisplayLabel); + getEventMap().put(event.getName(), event); + + // Persist the changes to the event list + saveEvents(); + + return new RecordsManagementEvent(eventType, eventName, eventDisplayLabel); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService#removeEvent(java.lang.String) + */ + public void removeEvent(String eventName) + { + // Remove the event from the map + getEventMap().remove(eventName); + + // Persist the changes to the event list + saveEvents(); + } + + /** + * Helper method to get the event map. Loads initial instance from persisted configuration file. + * + * @return Map map of available events by event name + */ + private Map getEventMap() + { + if (this.events == null) + { + loadEvents(); + } + return this.events; + } + + /** + * Load the events from the persistant storage + */ + private void loadEvents() + { + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Object doWork() throws Exception + { + // Get the event config node + if (nodeService.exists(CONFIG_NODE_REF) == false) + { + throw new AlfrescoRuntimeException("Unable to find records management event configuration node."); + } + + // Read content from config node + ContentReader reader = contentService.getReader(CONFIG_NODE_REF, ContentModel.PROP_CONTENT); + String jsonString = reader.getContentString(); + + JSONObject configJSON = new JSONObject(jsonString); + JSONArray eventsJSON = configJSON.getJSONArray("events"); + + events = new HashMap(eventsJSON.length()); + + for (int i = 0; i < eventsJSON.length(); i++) + { + // Get the JSON object that represents the event + JSONObject eventJSON = eventsJSON.getJSONObject(i); + + // Get the details of the event + String eventType = eventJSON.getString("eventType"); + String eventName = eventJSON.getString("eventName"); + String eventDisplayLabel = eventJSON.getString("eventDisplayLabel"); + + // Check that the eventType is valid + if (eventTypes.containsKey(eventType) == false) + { + throw new AlfrescoRuntimeException( + "Can not load rm event configuration because event " + + eventName + + " has an undefined eventType. (" + + eventType + ")"); + } + + // Create event and add to map + RecordsManagementEvent event = new RecordsManagementEvent(eventType, eventName, eventDisplayLabel); + events.put(event.getName(), event); + } + return null; + } + + }, AuthenticationUtil.getSystemUserName()); + } + + /** + * Save the events to the peristant storage + */ + private void saveEvents() + { + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Object doWork() throws Exception + { + // Get the event config node + if (nodeService.exists(CONFIG_NODE_REF) == false) + { + throw new AlfrescoRuntimeException("Unable to find records management event configuration node."); + } + + JSONObject configJSON = new JSONObject(); + JSONArray eventsJSON = new JSONArray(); + + int index = 0; + for (RecordsManagementEvent event : events.values()) + { + JSONObject eventJSON = new JSONObject(); + eventJSON.put("eventType", event.getType()); + eventJSON.put("eventName", event.getName()); + eventJSON.put("eventDisplayLabel", event.getDisplayLabel()); + + eventsJSON.put(index, eventJSON); + index++; + } + configJSON.put("events", eventsJSON); + + // Get content writer + ContentWriter contentWriter = contentService.getWriter(CONFIG_NODE_REF, ContentModel.PROP_CONTENT, true); + contentWriter.putContent(configJSON.toString()); + + return null; + } + + }, AuthenticationUtil.getSystemUserName()); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService#getEventType(java.lang.String) + */ + public RecordsManagementEventType getEventType(String eventTypeName) + { + return this.eventTypes.get(eventTypeName); + } +} + + diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/event/RecordsManagementEventType.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/event/RecordsManagementEventType.java new file mode 100644 index 0000000000..a87f9c8e83 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/event/RecordsManagementEventType.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.event; + +/** + * Records management event type interface + * + * @author Roy Wetherall + */ +public interface RecordsManagementEventType +{ + /** + * Get the name of the records management event type + * + * @return String event type name + */ + String getName(); + + /** + * Gets the display label of the event type + * + * @return String display label + */ + String getDisplayLabel(); + + /** + * Indicates whether the event is automatic or not + * + * @return boolean true if automatic, false otherwise + */ + boolean isAutomaticEvent(); +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/event/SimpleRecordsManagementEventTypeImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/event/SimpleRecordsManagementEventTypeImpl.java new file mode 100644 index 0000000000..1eef409477 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/event/SimpleRecordsManagementEventTypeImpl.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.event; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.surf.util.I18NUtil; +import org.springframework.beans.factory.BeanNameAware; + +/** + * Simple records management event type implementation + * + * @author Roy Wetherall + */ +public class SimpleRecordsManagementEventTypeImpl implements RecordsManagementEventType, BeanNameAware +{ + /** Logger */ + @SuppressWarnings("unused") + private static Log logger = LogFactory.getLog(SimpleRecordsManagementEventTypeImpl.class); + + /** Display label lookup prefix */ + protected static final String LOOKUP_PREFIX = "rmeventservice."; + + /** Name */ + public static final String NAME = "rmEventType.simple"; + + /** Records management event service */ + protected RecordsManagementEventService recordsManagementEventService; + + /** Name */ + protected String name; + + /** + * Set the records management event service + * + * @param recordsManagementEventService records management service + */ + public void setRecordsManagementEventService(RecordsManagementEventService recordsManagementEventService) + { + this.recordsManagementEventService = recordsManagementEventService; + } + + /** + * Initialisation method + */ + public void init() + { + recordsManagementEventService.registerEventType(this); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventType#isAutomaticEvent() + */ + public boolean isAutomaticEvent() + { + return false; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventType#getName() + */ + public String getName() + { + return this.name; + } + + /** + * @see org.springframework.beans.factory.BeanNameAware#setBeanName(java.lang.String) + */ + public void setBeanName(String name) + { + this.name = name; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventType#getDisplayLabel() + */ + public String getDisplayLabel() + { + return I18NUtil.getMessage(LOOKUP_PREFIX + getName()); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/forms/RecordsManagementFormFilter.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/forms/RecordsManagementFormFilter.java new file mode 100644 index 0000000000..9dddf5557d --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/forms/RecordsManagementFormFilter.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.forms; + +import java.util.List; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry; +import org.alfresco.module.org_alfresco_module_rm.dod5015.DOD5015Model; +import org.alfresco.repo.forms.FieldGroup; +import org.alfresco.repo.forms.Form; +import org.alfresco.repo.forms.FormData; +import org.alfresco.repo.forms.processor.AbstractFilter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.NamespaceService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Abstract base class for records management related form filter + * implementations. + * + * @author Gavin Cornwell + */ +public abstract class RecordsManagementFormFilter extends AbstractFilter +{ + /** Logger */ + @SuppressWarnings("unused") + private static Log logger = LogFactory.getLog(RecordsManagementFormFilter.class); + + public static final String CUSTOM_RM_FIELD_GROUP_ID = "rm-custom"; + + protected static final FieldGroup CUSTOM_RM_FIELD_GROUP = new FieldGroup(CUSTOM_RM_FIELD_GROUP_ID, null, false, + false, null); + + protected NamespaceService namespaceService; + protected NodeService nodeService; + protected RecordsManagementServiceRegistry rmServiceRegistry; + protected RecordsManagementService rmService; + protected RecordsManagementAdminService rmAdminService; + + /** + * Sets the NamespaceService instance + * + * @param namespaceService The NamespaceService instance + */ + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + /** + * Sets the node service + * + * @param nodeService The NodeService instance + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Sets the RecordsManagementServiceRegistry instance + * + * @param rmServiceRegistry The RecordsManagementServiceRegistry instance + */ + public void setRecordsManagementServiceRegistry(RecordsManagementServiceRegistry rmServiceRegistry) + { + this.rmServiceRegistry = rmServiceRegistry; + } + + /** + * Sets the RecordsManagementService instance + * + * @param rmService The RecordsManagementService instance + */ + public void setRecordsManagementService(RecordsManagementService rmService) + { + this.rmService = rmService; + } + + /** + * Sets the RecordsManagementAdminService instance + * + * @param rmAdminService The RecordsManagementAdminService instance + */ + public void setRecordsManagementAdminService(RecordsManagementAdminService rmAdminService) + { + this.rmAdminService = rmAdminService; + } + + /** + * @see + * org.alfresco.repo.forms.processor.Filter#beforePersist(java.lang.Object, + * org.alfresco.repo.forms.FormData) + */ + public void beforePersist(ItemType item, FormData data) + { + // ignored + } + + /** + * @see + * org.alfresco.repo.forms.processor.Filter#beforeGenerate(java.lang.Object, + * java.util.List, java.util.List, org.alfresco.repo.forms.Form, + * java.util.Map) + */ + public void beforeGenerate(ItemType item, List fields, List forcedFields, Form form, + Map context) + { + // ignored + } + + /** + * @see + * org.alfresco.repo.forms.processor.Filter#afterPersist(java.lang.Object, + * org.alfresco.repo.forms.FormData, java.lang.Object) + */ + public void afterPersist(ItemType item, FormData data, NodeRef persistedObject) + { + // ignored + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/forms/RecordsManagementNodeFormFilter.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/forms/RecordsManagementNodeFormFilter.java new file mode 100644 index 0000000000..b0a73bffda --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/forms/RecordsManagementNodeFormFilter.java @@ -0,0 +1,438 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.forms; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.model.ImapModel; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionScheduleImpl; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.module.org_alfresco_module_rm.dod5015.DOD5015Model; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementCustomModel; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.forms.Field; +import org.alfresco.repo.forms.FieldDefinition; +import org.alfresco.repo.forms.Form; +import org.alfresco.repo.forms.PropertyFieldDefinition; +import org.alfresco.repo.forms.processor.node.FieldUtils; +import org.alfresco.repo.forms.processor.node.FormFieldConstants; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Implementation of a form processor Filter. + *

+ * The filter ensures that any custom properties defined for the records + * management type are provided as part of the Form and also assigned to the + * same field group. + *

+ * + * @author Gavin Cornwell + */ +public class RecordsManagementNodeFormFilter extends RecordsManagementFormFilter implements RecordsManagementModel, DOD5015Model +{ + /** Logger */ + private static Log logger = LogFactory.getLog(RecordsManagementNodeFormFilter.class); + + protected static final String TRANSIENT_DECLARED = "rmDeclared"; + protected static final String TRANSIENT_RECORD_TYPE = "rmRecordType"; + protected static final String TRANSIENT_CATEGORY_ID = "rmCategoryIdentifier"; + protected static final String TRANSIENT_DISPOSITION_INSTRUCTIONS = "rmDispositionInstructions"; + + /** Dictionary service */ + protected DictionaryService dictionaryService; + + /** Disposition service */ + protected DispositionService dispositionService; + + /** + * Sets the data dictionary service + * + * @param dictionaryService The DictionaryService instance + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + /** + * Sets the disposition service + * + * @param dispositionService disposition service + */ + public void setDispositionService(DispositionService dispositionService) + { + this.dispositionService = dispositionService; + } + + /* + * @see org.alfresco.repo.forms.processor.Filter#afterGenerate(java.lang.Object, java.util.List, java.util.List, org.alfresco.repo.forms.Form, java.util.Map) + */ + public void afterGenerate(NodeRef nodeRef, List fields, List forcedFields, Form form, + Map context) + { + // TODO this needs a massive refactor inorder to support any custom type or aspect .... + + // if the node has the RM marker aspect look for the custom properties + // for the type + if (this.nodeService.hasAspect(nodeRef, ASPECT_FILE_PLAN_COMPONENT)) + { + // Make sure any customisable types or aspects present of the node have their properties included + // in the custom group + showCustomProperties(nodeRef, form); + + if (this.nodeService.hasAspect(nodeRef, ASPECT_RECORD)) + { + // force the "supplementalMarkingList" property to be present + forceSupplementalMarkingListProperty(form, nodeRef); + + // generate property definitions for the 'transient' properties + generateDeclaredPropertyField(form, nodeRef); + generateRecordTypePropertyField(form, nodeRef); + generateCategoryIdentifierPropertyField(form, nodeRef); + generateDispositionInstructionsPropertyField(form, nodeRef); + + // if the record is the result of an email we need to 'protect' some fields + if (this.nodeService.hasAspect(nodeRef, ImapModel.ASPECT_IMAP_CONTENT)) + { + protectEmailExtractedFields(form, nodeRef); + } + } + else + { + QName type = this.nodeService.getType(nodeRef); + if (TYPE_RECORD_FOLDER.equals(type)) + { + // force the "supplementalMarkingList" property to be present + forceSupplementalMarkingListProperty(form, nodeRef); + } + else if (TYPE_DISPOSITION_SCHEDULE.equals(type)) + { + // use the same mechanism used to determine whether steps can be removed from the + // schedule to determine whether the disposition level can be changed i.e. record + // level or folder level. + DispositionSchedule schedule = new DispositionScheduleImpl(this.rmServiceRegistry, this.nodeService, nodeRef); + if (dispositionService.hasDisposableItems(schedule) == true) + { + protectRecordLevelDispositionPropertyField(form); + } + } + } + } + } + + /** + * Show the custom properties if any are present. + * + * @param nodeRef node reference + * @param form form + */ + protected void showCustomProperties(NodeRef nodeRef, Form form) + { + Set customClasses = rmAdminService.getCustomisable(nodeRef); + if (customClasses.isEmpty() == false) + { + // add the 'rm-custom' field group + addCustomRMGroup(form); + } + } + + /** + * Adds the Custom RM field group (id 'rm-custom') to all the field + * definitions representing RM custom properties. + * + * @param form The form holding the field definitions + */ + protected void addCustomRMGroup(Form form) + { + // iterate round existing fields and set group on each custom + // RM field + List fieldDefs = form.getFieldDefinitions(); + for (FieldDefinition fieldDef : fieldDefs) + { + if (fieldDef.getName().startsWith(RM_CUSTOM_PREFIX) && + !fieldDef.getName().equals(PROP_SUPPLEMENTAL_MARKING_LIST.toPrefixString(this.namespaceService))) + { + // only add custom RM properties, not associations/references + if (fieldDef instanceof PropertyFieldDefinition) + { + fieldDef.setGroup(CUSTOM_RM_FIELD_GROUP); + + if (logger.isDebugEnabled()) + logger.debug("Added \"" + fieldDef.getName() + "\" to RM custom field group"); + } + } + } + } + + /** + * Forces the "rmc:supplementalMarkingList" property to be present, if it is + * already on the given node this method does nothing, otherwise a property + * field definition is generated for the property. + * + * @param form The Form instance to add the property to + * @param nodeRef The node the form is being generated for + */ + protected void forceSupplementalMarkingListProperty(Form form, NodeRef nodeRef) + { + if (!this.nodeService.hasAspect(nodeRef, + RecordsManagementCustomModel.ASPECT_SUPPLEMENTAL_MARKING_LIST)) + { + PropertyDefinition propDef = this.dictionaryService.getProperty( + RecordsManagementCustomModel.PROP_SUPPLEMENTAL_MARKING_LIST); + + if (propDef != null) + { + Field field = FieldUtils.makePropertyField(propDef, null, null, namespaceService); + form.addField(field); + } + else if (logger.isWarnEnabled()) + { + logger.warn("Could not add " + + RecordsManagementCustomModel.PROP_SUPPLEMENTAL_MARKING_LIST.getLocalName() + + " property as it's definition could not be found"); + } + } + } + + /** + * Generates the field definition for the transient rmDeclared + * property. + * + * @param form The Form instance to add the property to + * @param nodeRef The node the form is being generated for + */ + protected void generateDeclaredPropertyField(Form form, NodeRef nodeRef) + { + // TODO should this be done using a new FieldProcessor? + String dataKeyName = FormFieldConstants.PROP_DATA_PREFIX + TRANSIENT_DECLARED; + PropertyFieldDefinition declaredField = new PropertyFieldDefinition(TRANSIENT_DECLARED, + DataTypeDefinition.BOOLEAN.getLocalName()); + declaredField.setLabel(TRANSIENT_DECLARED); + declaredField.setDescription(TRANSIENT_DECLARED); + declaredField.setProtectedField(true); + declaredField.setDataKeyName(dataKeyName); + form.addFieldDefinition(declaredField); + form.addData(dataKeyName, this.nodeService.hasAspect(nodeRef, ASPECT_DECLARED_RECORD)); + } + + /** + * Generates the field definition for the transient + * rmRecordType property + * + * @param form The Form instance to add the property to + * @param nodeRef The node the form is being generated for + */ + protected void generateRecordTypePropertyField(Form form, NodeRef nodeRef) + { + String dataKeyName = FormFieldConstants.PROP_DATA_PREFIX + TRANSIENT_RECORD_TYPE; + PropertyFieldDefinition recordTypeField = new PropertyFieldDefinition(TRANSIENT_RECORD_TYPE, + DataTypeDefinition.TEXT.getLocalName()); + recordTypeField.setLabel(TRANSIENT_RECORD_TYPE); + recordTypeField.setDescription(TRANSIENT_RECORD_TYPE); + recordTypeField.setProtectedField(true); + recordTypeField.setDataKeyName(dataKeyName); + form.addFieldDefinition(recordTypeField); + + // determine what record type value to return, use aspect/type title + // from model + String recordType = null; + QName type = this.nodeService.getType(nodeRef); + if (TYPE_NON_ELECTRONIC_DOCUMENT.equals(type)) + { + // get the non-electronic type title + recordType = dictionaryService.getType(TYPE_NON_ELECTRONIC_DOCUMENT).getTitle(); + } + else + { + // the aspect applied to record determines it's type + if (nodeService.hasAspect(nodeRef, ASPECT_PDF_RECORD)) + { + recordType = dictionaryService.getAspect(ASPECT_PDF_RECORD).getTitle(); + } + else if (nodeService.hasAspect(nodeRef, ASPECT_WEB_RECORD)) + { + recordType = dictionaryService.getAspect(ASPECT_WEB_RECORD).getTitle(); + } + else if (nodeService.hasAspect(nodeRef, ASPECT_SCANNED_RECORD)) + { + recordType = dictionaryService.getAspect(ASPECT_SCANNED_RECORD).getTitle(); + } + else if (nodeService.hasAspect(nodeRef, ASPECT_DIGITAL_PHOTOGRAPH_RECORD)) + { + recordType = dictionaryService.getAspect(ASPECT_DIGITAL_PHOTOGRAPH_RECORD).getTitle(); + } + else + { + // no specific aspect applied so default to just "Record" + recordType = dictionaryService.getAspect(ASPECT_RECORD).getTitle(); + } + } + + form.addData(dataKeyName, recordType); + } + + /** + * Generates the field definition for the transient rmCategoryIdentifier + * property + * + * @param form The Form instance to add the property to + * @param nodeRef The node the form is being generated for + */ + protected void generateDispositionInstructionsPropertyField(Form form, NodeRef nodeRef) + { + String dataKeyName = FormFieldConstants.PROP_DATA_PREFIX + TRANSIENT_DISPOSITION_INSTRUCTIONS; + PropertyFieldDefinition dispInstructionsField = new PropertyFieldDefinition(TRANSIENT_DISPOSITION_INSTRUCTIONS, + DataTypeDefinition.TEXT.getLocalName()); + dispInstructionsField.setLabel(TRANSIENT_DISPOSITION_INSTRUCTIONS); + dispInstructionsField.setDescription(TRANSIENT_DISPOSITION_INSTRUCTIONS); + dispInstructionsField.setProtectedField(true); + dispInstructionsField.setDataKeyName(dataKeyName); + form.addFieldDefinition(dispInstructionsField); + + // use RMService to get disposition instructions + DispositionSchedule ds = dispositionService.getDispositionSchedule(nodeRef); + if (ds != null) + { + String instructions = ds.getDispositionInstructions(); + if (instructions != null) + { + form.addData(dataKeyName, instructions); + } + } + } + + /** + * Generates the field definition for the transient rmCategoryIdentifier + * property + * + * @param form The Form instance to add the property to + * @param nodeRef The node the form is being generated for + */ + protected void generateCategoryIdentifierPropertyField(Form form, NodeRef nodeRef) + { + String dataKeyName = FormFieldConstants.PROP_DATA_PREFIX + TRANSIENT_CATEGORY_ID; + PropertyFieldDefinition categoryIdField = new PropertyFieldDefinition(TRANSIENT_CATEGORY_ID, + DataTypeDefinition.TEXT.getLocalName()); + categoryIdField.setLabel(TRANSIENT_CATEGORY_ID); + categoryIdField.setDescription(TRANSIENT_CATEGORY_ID); + categoryIdField.setProtectedField(true); + categoryIdField.setDataKeyName(dataKeyName); + form.addFieldDefinition(categoryIdField); + + // get the category id from the appropriate parent node + NodeRef category = getRecordCategory(nodeRef); + if (category != null) + { + String categoryId = (String)nodeService.getProperty(category, PROP_IDENTIFIER); + if (categoryId != null) + { + form.addData(dataKeyName, categoryId); + } + } + } + + /** + * Marks all the fields that contain data extracted from an email + * as protected fields. + * + * @param form The Form instance to add the property to + * @param nodeRef The node the form is being generated for + */ + protected void protectEmailExtractedFields(Form form, NodeRef nodeRef) + { + // iterate round existing fields and set email fields as protected + List fieldDefs = form.getFieldDefinitions(); + for (FieldDefinition fieldDef : fieldDefs) + { + if (fieldDef.getName().equals("cm:title") || + fieldDef.getName().equals("cm:author") || + fieldDef.getName().equals("rma:originator") || + fieldDef.getName().equals("rma:publicationDate") || + fieldDef.getName().equals("rma:dateReceived") || + fieldDef.getName().equals("rma:address") || + fieldDef.getName().equals("rma:otherAddress")) + { + fieldDef.setProtectedField(true); + } + } + + if (logger.isDebugEnabled()) + logger.debug("Set email related fields to be protected"); + } + + /** + * Marks the recordLevelDisposition property as protected to disable editing + * + * @param form The Form instance + */ + protected void protectRecordLevelDispositionPropertyField(Form form) + { + // iterate round existing fields and set email fields as protected + List fieldDefs = form.getFieldDefinitions(); + for (FieldDefinition fieldDef : fieldDefs) + { + if (fieldDef.getName().equals(RecordsManagementModel.PROP_RECORD_LEVEL_DISPOSITION.toPrefixString( + this.namespaceService))) + { + fieldDef.setProtectedField(true); + break; + } + } + + if (logger.isDebugEnabled()) + logger.debug("Set 'rma:recordLevelDisposition' field to be protected as record folders or records are present"); + } + + /** + * Retrieves the record category the given record belongs to or + * null if the record category can not be found + * + * @param record NodeRef representing the record + * @return NodeRef of the record's category + */ + protected NodeRef getRecordCategory(NodeRef record) + { + NodeRef result = null; + + NodeRef parent = this.nodeService.getPrimaryParent(record).getParentRef(); + if (parent != null) + { + QName nodeType = this.nodeService.getType(parent); + if (this.dictionaryService.isSubClass(nodeType, TYPE_RECORD_CATEGORY)) + { + result = parent; + } + else + { + result = getRecordCategory(parent); + } + } + + return result; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/forms/RecordsManagementTypeFormFilter.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/forms/RecordsManagementTypeFormFilter.java new file mode 100644 index 0000000000..b586652f4a --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/forms/RecordsManagementTypeFormFilter.java @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.forms; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.identifier.IdentifierService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.forms.Field; +import org.alfresco.repo.forms.FieldDefinition; +import org.alfresco.repo.forms.FieldGroup; +import org.alfresco.repo.forms.Form; +import org.alfresco.repo.forms.FormData; +import org.alfresco.repo.forms.processor.node.FieldUtils; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.surf.util.ParameterCheck; + +/** + * Implementation of a form processor Filter. + *

+ * The filter implements the afterGenerate method to ensure a + * default unique identifier is provided for the rma:identifier + * property. + *

+ *

+ * The filter also ensures that any custom properties defined for the records + * management type are provided as part of the Form. + *

+ * + * @author Gavin Cornwell + */ +public class RecordsManagementTypeFormFilter extends RecordsManagementFormFilter implements RecordsManagementModel +{ + /** Logger */ + private static Log logger = LogFactory.getLog(RecordsManagementTypeFormFilter.class); + + protected static final String NAME_FIELD_GROUP_ID = "name"; + protected static final String TITLE_FIELD_GROUP_ID = "title"; + protected static final String DESC_FIELD_GROUP_ID = "description"; + protected static final String OTHER_FIELD_GROUP_ID = "other"; + + protected static final FieldGroup NAME_FIELD_GROUP = new FieldGroup(NAME_FIELD_GROUP_ID, null, false, false, null); + protected static final FieldGroup TITLE_FIELD_GROUP = new FieldGroup(TITLE_FIELD_GROUP_ID, null, false, false, null); + protected static final FieldGroup DESC_FIELD_GROUP = new FieldGroup(DESC_FIELD_GROUP_ID, null, false, false, null); + protected static final FieldGroup OTHER_FIELD_GROUP = new FieldGroup(OTHER_FIELD_GROUP_ID, null, false, false, null); + + /** Identifier service */ + protected IdentifierService identifierService; + + /** + * @param identifierService identifier service + */ + public void setIdentifierService(IdentifierService identifierService) + { + this.identifierService = identifierService; + } + + /* + * @see + * org.alfresco.repo.forms.processor.Filter#afterGenerate(java.lang.Object, + * java.util.List, java.util.List, org.alfresco.repo.forms.Form, + * java.util.Map) + */ + public void afterGenerate(TypeDefinition type, List fields, List forcedFields, Form form, + Map context) + { + QName typeName = type.getName(); + if (rmAdminService.isCustomisable(typeName) == true) + { + addCustomRMProperties(typeName, form); + } + + // What about any mandatory aspects? + Set aspects = type.getDefaultAspectNames(); + for (QName aspect : aspects) + { + if (rmAdminService.isCustomisable(aspect) == true) + { + addCustomRMProperties(aspect, form); + } + } + + // Group fields + groupFields(form); + } + + /** + * Adds a property definition for each of the custom properties for the + * given RM type to the given form. + * + * @param rmTypeCustomAspect Enum representing the RM type to add custom + * properties for + * @param form The form to add the properties to + */ + protected void addCustomRMProperties(QName customisableType, Form form) + { + ParameterCheck.mandatory("customisableType", customisableType); + ParameterCheck.mandatory("form", form); + + Map customProps = rmAdminService.getCustomPropertyDefinitions(customisableType); + + if (customProps != null) + { + if (logger.isDebugEnabled() == true) + { + logger.debug("Found " + customProps.size() + " custom properties for customisable type " + customisableType); + } + + // setup field definition for each custom property + Collection properties = customProps.values(); + List fields = FieldUtils.makePropertyFields(properties, CUSTOM_RM_FIELD_GROUP, namespaceService); + form.addFields(fields); + } + } + + /* + * @see org.alfresco.repo.forms.processor.Filter#afterPersist(java.lang.Object, org.alfresco.repo.forms.FormData, java.lang.Object) + */ + public void afterPersist(TypeDefinition item, FormData data, final NodeRef nodeRef) + { + } + + /** + * Generates a unique identifier for the given node (based on the dbid). + * + * @param nodeRef The NodeRef to generate a unique id for + * @return The identifier + */ + protected String generateIdentifier(NodeRef nodeRef) + { + String identifier = identifierService.generateIdentifier(nodeRef); + if (logger.isDebugEnabled() == true) + { + logger.debug("Generated '" + identifier + "' for unique identifier"); + } + return identifier; + } + + /** + * Function to pad a string with zero '0' characters to the required length + * + * @param s String to pad with leading zero '0' characters + * @param len Length to pad to + * @return padded string or the original if already at >=len characters + */ + protected String padString(String s, int len) + { + String result = s; + + for (int i = 0; i < (len - s.length()); i++) + { + result = "0" + result; + } + + return result; + } + + /** + * Puts all fields in a group to workaround ALF-6089. + * + * @param form The form being generated + */ + protected void groupFields(Form form) + { + // to control the order of the fields add the name, title and description fields to + // a field group containing just that field, all other fields that are not already + // in a group go into an "other" field group. The client config can then declare a + // client side set with the same id and order them correctly. + + List fieldDefs = form.getFieldDefinitions(); + for (FieldDefinition fieldDef : fieldDefs) + { + FieldGroup group = fieldDef.getGroup(); + if (group == null) + { + if (fieldDef.getName().equals(ContentModel.PROP_NAME.toPrefixString(this.namespaceService))) + { + fieldDef.setGroup(NAME_FIELD_GROUP); + } + else if (fieldDef.getName().equals(ContentModel.PROP_TITLE.toPrefixString(this.namespaceService))) + { + fieldDef.setGroup(TITLE_FIELD_GROUP); + } + else if (fieldDef.getName().equals(ContentModel.PROP_DESCRIPTION.toPrefixString(this.namespaceService))) + { + fieldDef.setGroup(DESC_FIELD_GROUP); + } + else + { + fieldDef.setGroup(OTHER_FIELD_GROUP); + } + } + } + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/identifier/BasicIdentifierGenerator.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/identifier/BasicIdentifierGenerator.java new file mode 100644 index 0000000000..42165ad5b5 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/identifier/BasicIdentifierGenerator.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.identifier; + +import java.io.Serializable; +import java.util.Calendar; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Basic identifier generator implementation. + * + * @author Roy Wetherall + */ +public class BasicIdentifierGenerator extends IdentifierGeneratorBase +{ + /** + * @see org.alfresco.module.org_alfresco_module_rm.identifier.IdentifierGenerator#generateId(java.util.Map) + */ + @Override + public String generateId(Map context) + { + NodeRef nodeRef = (NodeRef)context.get(IdentifierService.CONTEXT_NODEREF); + Long dbId = 0l; + if (nodeRef != null) + { + dbId = (Long)nodeService.getProperty(nodeRef, ContentModel.PROP_NODE_DBID); + } + else + { + dbId = System.currentTimeMillis(); + } + + Calendar fileCalendar = Calendar.getInstance(); + String year = Integer.toString(fileCalendar.get(Calendar.YEAR)); + String identifier = year + "-" + padString(dbId.toString(), 10); + return identifier; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/identifier/IdentifierGenerator.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/identifier/IdentifierGenerator.java new file mode 100644 index 0000000000..666c2b353c --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/identifier/IdentifierGenerator.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.identifier; + +import java.io.Serializable; +import java.util.Map; + +import org.alfresco.service.namespace.QName; + +/** + * Generates an identifier for a content type from a given context. + * + * @author Roy Wetherall + */ +public interface IdentifierGenerator +{ + /** + * The content type this generator is applicible to. + * @return QName the type + */ + QName getType(); + + /** + * Generates the next id based on the provided context. + * @param context map of context values + * @return String the next id + */ + String generateId(Map context); +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/identifier/IdentifierGeneratorBase.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/identifier/IdentifierGeneratorBase.java new file mode 100644 index 0000000000..c875210571 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/identifier/IdentifierGeneratorBase.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.identifier; + +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; + +/** + * @author Roy Wetherall + */ +public abstract class IdentifierGeneratorBase implements IdentifierGenerator +{ + /** Identifier service */ + private IdentifierService identifierService; + + /** Node service */ + protected NodeService nodeService; + + /** Content type */ + private QName type; + + /** + * Initialisation method + */ + public void init() + { + identifierService.register(this); + } + + /** + * Set identifier service. + * + * @param identifierService identifier service + */ + public void setIdentifierService(IdentifierService identifierService) + { + this.identifierService = identifierService; + } + + /** + * Set the node service + * + * @param nodeService node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Set type. + * + * @param type content type + */ + public void setTypeAsString(String type) + { + this.type = QName.createQName(type); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.identifier.IdentifierGenerator#getType() + */ + @Override + public QName getType() + { + return type; + } + + /** + * Function to pad a string with zero '0' characters to the required length + * + * @param s String to pad with leading zero '0' characters + * @param len Length to pad to + * @return padded string or the original if already at >=len characters + */ + protected String padString(String s, int len) + { + String result = s; + + for (int i = 0; i < (len - s.length()); i++) + { + result = "0" + result; + } + + return result; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/identifier/IdentifierService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/identifier/IdentifierService.java new file mode 100644 index 0000000000..de9ad851df --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/identifier/IdentifierService.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.identifier; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * Records management identifier service + * + * @author Roy Wetherall + */ +public interface IdentifierService +{ + /** Context value names */ + public static final String CONTEXT_NODEREF = "noderef"; + public static final String CONTEXT_PARENT_NODEREF = "parentndoeref"; + public static final String CONTEXT_ORIG_TYPE = "origionaltype"; + + /** + * Register an identifier generator implementation with the service. + * + * @param identifierGenerator identifier generator implementation + */ + void register(IdentifierGenerator identifierGenerator); + + /** + * Generate an identifier for a node with the given type and parent. + * + * @param type type of the node + * @param parent parent of the ndoe + * @return String generated identifier + */ + String generateIdentifier(QName type, NodeRef parent); + + /** + * Generate an identifier for the given node. + * + * @param nodeRef node reference + * @return String generated identifier + */ + String generateIdentifier(NodeRef nodeRef); +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/identifier/IdentifierServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/identifier/IdentifierServiceImpl.java new file mode 100644 index 0000000000..e9d99efd5f --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/identifier/IdentifierServiceImpl.java @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.identifier; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ParameterCheck; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * @author Roy Wetherall + */ +public class IdentifierServiceImpl implements IdentifierService +{ + /** Logger */ + private static Log logger = LogFactory.getLog(IdentifierServiceImpl.class); + + /** Registry map */ + private Map register = new HashMap(5); + + /** Node service */ + private NodeService nodeService; + + /** Dictionary service */ + private DictionaryService dictionaryService; + + /** + * Set the node service + * + * @param nodeService node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Set the dictionary service + * + * @param dictionaryService dictionary service + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.identifier.IdentifierService#generateIdentifier(org.alfresco.service.namespace.QName, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public String generateIdentifier(QName type, NodeRef parent) + { + ParameterCheck.mandatory("type", type); + + // Build the context + Map context = new HashMap(2); + if (parent != null) + { + context.put(CONTEXT_PARENT_NODEREF, parent); + } + context.put(CONTEXT_ORIG_TYPE, type); + + // Generate the id + return generateIdentifier(type, context); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.identifier.IdentifierService#generateIdentifier(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public String generateIdentifier(NodeRef nodeRef) + { + ParameterCheck.mandatory("nodeRef", nodeRef); + + Map context = new HashMap(3); + + // Set the original type + QName type = nodeService.getType(nodeRef); + context.put(CONTEXT_ORIG_TYPE, type); + + // Set the parent reference + ChildAssociationRef assocRef = nodeService.getPrimaryParent(nodeRef); + if (assocRef != null && assocRef.getParentRef() != null) + { + context.put(CONTEXT_PARENT_NODEREF, assocRef.getParentRef()); + } + + // Set the node reference + context.put(CONTEXT_NODEREF, nodeRef); + + // Generate the identifier + return generateIdentifier(type, context); + + } + + /** + * Generate an identifier for a given type of object with the acompanying context. + * + * @param type content type + * @param context context + * @return String identifier + */ + private String generateIdentifier(QName type, Map context) + { + ParameterCheck.mandatory("type", type); + ParameterCheck.mandatory("context", context); + + // Get the identifier generator + IdentifierGenerator idGen = lookupGenerator(type); + if (idGen == null) + { + if (logger.isDebugEnabled() == true) + { + logger.debug("Unable to generate id for object of type " + type.toString() + ", because no identifier generator was found."); + } + throw new AlfrescoRuntimeException("Unable to generate id for object of type " + type.toString() + ", because no identifier generator was found."); + } + + // Generate the identifier + return idGen.generateId(context); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.identifier.IdentifierService#register(org.alfresco.module.org_alfresco_module_rm.identifier.IdentifierGenerator) + */ + public void register(IdentifierGenerator idGen) + { + register.put(idGen.getType(), idGen); + } + + /** + * + * @param type content type (could be aspect or type) + * @return + */ + private IdentifierGenerator lookupGenerator(QName type) + { + ParameterCheck.mandatory("type", type); + + if (logger.isDebugEnabled() == true) + { + logger.debug("Looking for idGenerator for type " + type.toString()); + } + + // Look for the generator related to the type + IdentifierGenerator result = register.get(type); + if (result == null) + { + // Check the parent type + ClassDefinition typeDef = dictionaryService.getClass(type); + if (typeDef != null) + { + QName parentType = typeDef.getParentName(); + if (parentType != null) + { + // Recurse to find parent type generator + result = lookupGenerator(parentType); + } + } + else + { + if (logger.isDebugEnabled() == true) + { + logger.debug("Unable to find type definition for " + type.toString() + " when generating identifier."); + } + } + } + return result; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/DispositionLifecycleJob.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/DispositionLifecycleJob.java new file mode 100644 index 0000000000..8f5b3d51ea --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/DispositionLifecycleJob.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2009-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.module.org_alfresco_module_rm.job; + +import java.util.List; + +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.transaction.TransactionService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; + +/** + * The Disposition Lifecycle Job Finds all disposition action nodes which are + * for "retain" or "cutOff" actions Where asOf > now OR + * dispositionEventsEligible = true; + * + * Runs the cut off or retain action for + * elligible records. + * + * @author mrogers + */ +public class DispositionLifecycleJob implements Job +{ + private static Log logger = LogFactory.getLog(DispositionLifecycleJob.class); + + /** + * @see org.quartz.Job#execute(org.quartz.JobExecutionContext) + */ + public void execute(JobExecutionContext context) throws JobExecutionException + { + final RecordsManagementActionService rmActionService = (RecordsManagementActionService) context + .getJobDetail().getJobDataMap().get("recordsManagementActionService"); + final NodeService nodeService = (NodeService) context.getJobDetail().getJobDataMap().get( + "nodeService"); + final SearchService search = (SearchService) context.getJobDetail().getJobDataMap().get( + "searchService"); + final TransactionService trxService = (TransactionService) context.getJobDetail() + .getJobDataMap().get("transactionService"); + + logger.debug("Job Starting"); + + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() throws Exception + { + StringBuilder sb = new StringBuilder(); + sb.append("+TYPE:\"rma:dispositionAction\" "); + sb.append("+(@rma\\:dispositionAction:(\"cutoff\" OR \"retain\"))"); + sb.append("+ISNULL:\"rma:dispositionActionCompletedAt\" "); + sb.append("+( "); + sb.append("@rma\\:dispositionEventsEligible:true "); + sb.append("OR @rma\\:dispositionAsOf:[MIN TO NOW] "); + sb.append(") "); + + String query = sb.toString(); + + ResultSet results = search.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, + SearchService.LANGUAGE_LUCENE, query); + List resultNodes = results.getNodeRefs(); + results.close(); + + RetryingTransactionHelper trn = trxService.getRetryingTransactionHelper(); + + for (NodeRef node : resultNodes) + { + final NodeRef currentNode = node; + + RetryingTransactionCallback processTranCB = new RetryingTransactionCallback() + { + public Boolean execute() throws Throwable + { + final String dispAction = (String) nodeService.getProperty(currentNode, + RecordsManagementModel.PROP_DISPOSITION_ACTION); + + // Run "retain" and "cutoff" actions. + + if (dispAction != null) + { + if (dispAction.equalsIgnoreCase("cutoff") || + dispAction.equalsIgnoreCase("retain")) + { + ChildAssociationRef parent = nodeService.getPrimaryParent(currentNode); + if (parent.getTypeQName().equals(RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION)) + { + // Check that the action is executable + RecordsManagementAction rmAction = rmActionService.getDispositionAction(dispAction); + if (rmAction.isExecutable(parent.getParentRef(), null) == true) + { + rmActionService.executeRecordsManagementAction(parent.getParentRef(), dispAction); + if (logger.isDebugEnabled()) + { + logger.debug("Processed action: " + dispAction + "on" + parent); + } + } + else + { + logger.debug("The disposition action " + dispAction + " is not executable."); + } + } + return null; + } + } + return Boolean.TRUE; + } + }; + /** + * Now do the work, one action in each transaction + */ + trn.doInTransaction(processTranCB); + } + return null; + }; + + }, AuthenticationUtil.getSystemUserName()); + + logger.debug("Job Finished"); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/NotifyOfRecordsDueForReviewJob.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/NotifyOfRecordsDueForReviewJob.java new file mode 100644 index 0000000000..8b29d60e0b --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/NotifyOfRecordsDueForReviewJob.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2009-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.module.org_alfresco_module_rm.job; + +import java.util.List; + +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.notification.RecordsManagementNotificationHelper; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.transaction.TransactionService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; + +/** + * This job finds all Vital Records which are due for review, optionally + * excluding those for which notification has already been issued. + * + * @author Neil McErlean + */ +public class NotifyOfRecordsDueForReviewJob implements Job +{ + private static Log logger = LogFactory.getLog(NotifyOfRecordsDueForReviewJob.class); + + /** + * @see org.quartz.Job#execute(org.quartz.JobExecutionContext) + */ + public void execute(JobExecutionContext context) throws JobExecutionException + { + final RecordsManagementNotificationHelper notificationHelper = (RecordsManagementNotificationHelper)context.getJobDetail().getJobDataMap().get("recordsManagementNotificationHelper"); + final NodeService nodeService = (NodeService) context.getJobDetail().getJobDataMap().get("nodeService"); + final SearchService searchService = (SearchService) context.getJobDetail().getJobDataMap().get("searchService"); + final TransactionService trxService = (TransactionService) context.getJobDetail().getJobDataMap().get("transactionService"); + + if (logger.isDebugEnabled()) + { + logger.debug("Job " + this.getClass().getSimpleName() + " starting."); + } + + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() throws Exception + { + // Query is for all records that are due for review and for which + // notification has not been sent. + StringBuilder queryBuffer = new StringBuilder(); + queryBuffer.append("+ASPECT:\"rma:vitalRecord\" "); + queryBuffer.append("+(@rma\\:reviewAsOf:[MIN TO NOW] ) "); + queryBuffer.append("+( "); + queryBuffer.append("@rma\\:notificationIssued:false "); + queryBuffer.append("OR ISNULL:\"rma:notificationIssued\" "); + queryBuffer.append(") "); + String query = queryBuffer.toString(); + + ResultSet results = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_LUCENE, query); + final List resultNodes = results.getNodeRefs(); + results.close(); + + if (logger.isDebugEnabled() == true) + { + logger.debug("Found " + resultNodes.size() + " nodes due for review and without notification."); + } + + //If we have something to do and a template to do it with + if(resultNodes.size() != 0) + { + RetryingTransactionHelper trn = trxService.getRetryingTransactionHelper(); + + //Send the email message - but we must not retry since email is not transactional + RetryingTransactionCallback txCallbackSendEmail = new RetryingTransactionCallback() + { + // Set the notification issued property. + public Boolean execute() throws Throwable + { + // Send notification + notificationHelper.recordsDueForReviewEmailNotification(resultNodes); + + return null; + } + }; + + RetryingTransactionCallback txUpdateNodesCallback = new RetryingTransactionCallback() + { + // Set the notification issued property. + public Boolean execute() throws Throwable + { + for (NodeRef node : resultNodes) + { + nodeService.setProperty(node, RecordsManagementModel.PROP_NOTIFICATION_ISSUED, "true"); + } + return Boolean.TRUE; + } + }; + + /** + * Now do the work, one action in each transaction + */ + trn.setMaxRetries(0); // don't retry the send email + trn.doInTransaction(txCallbackSendEmail); + trn.setMaxRetries(10); + trn.doInTransaction(txUpdateNodesCallback); + } + return null; + } + + }, AuthenticationUtil.getSystemUserName()); + + if (logger.isDebugEnabled()) + { + logger.debug("Job " + this.getClass().getSimpleName() + " finished"); + } + } // end of execute method + +} + + diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/PublishUpdatesJob.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/PublishUpdatesJob.java new file mode 100644 index 0000000000..0d998729e2 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/PublishUpdatesJob.java @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2009-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.module.org_alfresco_module_rm.job; + +import java.util.List; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.module.org_alfresco_module_rm.job.publish.PublishExecutor; +import org.alfresco.module.org_alfresco_module_rm.job.publish.PublishExecutorRegistry; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.policy.BehaviourFilter; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.quartz.Job; +import org.quartz.JobDataMap; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; + +/** + * Job to publish any pending updates on marked node references. + * + * @author Roy Wetherall + */ +public class PublishUpdatesJob implements Job, RecordsManagementModel +{ + /** Logger */ + private static Log logger = LogFactory.getLog(PublishUpdatesJob.class); + + /** Node service */ + private NodeService nodeService; + + /** Search service */ + private SearchService searchService; + + /** Retrying transaction helper */ + private RetryingTransactionHelper retryingTransactionHelper; + + /** Publish executor register */ + private PublishExecutorRegistry register; + + /** Behaviour filter */ + private BehaviourFilter behaviourFilter; + + /** Indicates whether the job bean has been initialised or not */ + private boolean initialised = false; + + /** + * @see org.quartz.Job#execute(org.quartz.JobExecutionContext) + */ + public void execute(JobExecutionContext context) throws JobExecutionException + { + // Initialise the service references + initServices(context); + + if (logger.isDebugEnabled() == true) + { + logger.debug("Job Starting"); + } + + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() throws Exception + { + // Get a list of the nodes that have updates that need to be published + List nodeRefs = getUpdatedNodes(); + + // Deal with each updated disposition action in turn + for (NodeRef nodeRef : nodeRefs) + { + // Mark the update node as publishing in progress + markPublishInProgress(nodeRef); + try + { + // Publish updates + publishUpdates(nodeRef); + } + finally + { + // Ensure the update node has either completed the publish or is marked as no longer in progress + unmarkPublishInProgress(nodeRef); + } + } + return null; + }; + }, AuthenticationUtil.getSystemUserName()); + + if (logger.isDebugEnabled() == true) + { + logger.debug("Job Finished"); + } + } + + /** + * Get a list of the nodes with updates pending publish + * @return List list of node refences with updates pending publication + */ + private List getUpdatedNodes() + { + RetryingTransactionCallback> execution = + new RetryingTransactionHelper.RetryingTransactionCallback>() + { + @Override + public List execute() throws Throwable + { + // Build the query string + StringBuilder sb = new StringBuilder(); + sb.append("+ASPECT:\"rma:").append(ASPECT_UNPUBLISHED_UPDATE.getLocalName()).append("\" "); + sb.append("@rma\\:").append(PROP_PUBLISH_IN_PROGRESS.getLocalName()).append(":false "); + String query = sb.toString(); + + if (logger.isDebugEnabled() == true) + { + logger.debug("Executing query " + query); + } + + // Execute query to find updates awaiting publishing + ResultSet results = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, + SearchService.LANGUAGE_LUCENE, query); + List resultNodes = results.getNodeRefs(); + results.close(); + + if (logger.isDebugEnabled() == true) + { + logger.debug("Found " + resultNodes.size() + " disposition action definitions updates awaiting publishing."); + } + + return resultNodes; + } + }; + return retryingTransactionHelper.doInTransaction(execution, true); + } + + /** + * Initialise service based on the job execution context + * @param context job execution context + */ + private void initServices(JobExecutionContext context) + { + if (initialised == false) + { + // Get references to the required services + JobDataMap jobDataMap = context.getJobDetail().getJobDataMap(); + nodeService = (NodeService)jobDataMap.get("nodeService"); + searchService = (SearchService)jobDataMap.get("searchService"); + retryingTransactionHelper = (RetryingTransactionHelper)jobDataMap.get("retryingTransactionHelper"); + register = (PublishExecutorRegistry)jobDataMap.get("publishExecutorRegistry"); + behaviourFilter = (BehaviourFilter)jobDataMap.get("behaviourFilter"); + initialised = true; + } + } + + /** + * Mark the node as publish in progress. This is often used as a marker to prevent any further updates + * to a node. + * @param nodeRef node reference + */ + private void markPublishInProgress(final NodeRef nodeRef) + { + RetryingTransactionHelper.RetryingTransactionCallback execution = + new RetryingTransactionHelper.RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + if (logger.isDebugEnabled() == true) + { + logger.debug("Marking updated node as publish in progress. (node=" + nodeRef.toString() + ")"); + } + + behaviourFilter.disableBehaviour(nodeRef, TYPE_DISPOSITION_ACTION_DEFINITION); + try + { + // Mark the node as publish in progress + nodeService.setProperty(nodeRef, PROP_PUBLISH_IN_PROGRESS, true); + } + finally + { + behaviourFilter.enableBehaviour(nodeRef, TYPE_DISPOSITION_ACTION_DEFINITION); + } + return null; + } + }; + retryingTransactionHelper.doInTransaction(execution, false, true); + } + + /** + * Publish the updates made to the node. + * @param nodeRef node reference + */ + private void publishUpdates(final NodeRef nodeRef) + { + RetryingTransactionHelper.RetryingTransactionCallback execution = + new RetryingTransactionHelper.RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + behaviourFilter.disableBehaviour(nodeRef, TYPE_DISPOSITION_ACTION_DEFINITION); + try + { + // Get the update to value for the node + String updateTo = (String)nodeService.getProperty(nodeRef, PROP_UPDATE_TO); + + if (updateTo != null) + { + if (logger.isDebugEnabled() == true) + { + logger.debug("Node update to " + updateTo + " (noderef=" + nodeRef.toString() + ")"); + } + + // Get the publish executor + PublishExecutor executor = register.get(updateTo); + if (executor == null) + { + if (logger.isDebugEnabled() == true) + { + logger.debug("Unable to find a corresponding publish executor. (noderef=" + nodeRef.toString() + ", updateTo=" + updateTo + ")"); + } + throw new AlfrescoRuntimeException("Unable to find a corresponding publish executor. (noderef=" + nodeRef.toString() + ", updateTo=" + updateTo + ")"); + } + + if (logger.isDebugEnabled() == true) + { + logger.debug("Attempting to publish updates. (nodeRef=" + nodeRef.toString() + ")"); + } + + // Publish + executor.publish(nodeRef); + } + else + { + if (logger.isDebugEnabled() == true) + { + logger.debug("Unable to publish, because publish executor is not set."); + } + } + + // Remove the unpublished update aspect + nodeService.removeAspect(nodeRef, ASPECT_UNPUBLISHED_UPDATE); + + if (logger.isDebugEnabled() == true) + { + logger.debug("Publish updates complete. (nodeRef=" + nodeRef.toString() + ")"); + } + } + finally + { + behaviourFilter.enableBehaviour(nodeRef, TYPE_DISPOSITION_ACTION_DEFINITION); + } + + return null; + } + }; + retryingTransactionHelper.doInTransaction(execution); + } + + /** + * Unmark node as publish in progress, assuming publish failed. + * @param nodeRef node reference + */ + private void unmarkPublishInProgress(final NodeRef nodeRef) + { + RetryingTransactionHelper.RetryingTransactionCallback execution = + new RetryingTransactionHelper.RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + behaviourFilter.disableBehaviour(nodeRef, TYPE_DISPOSITION_ACTION_DEFINITION); + try + { + // Assuming the node still has unpublished information, then unmark it in progress + if (nodeService.exists(nodeRef) == true && + nodeService.hasAspect(nodeRef, ASPECT_UNPUBLISHED_UPDATE) == true) + { + if (logger.isDebugEnabled() == true) + { + logger.debug("Removing publish in progress marker from updated node, because update was not successful. (node=" + nodeRef.toString() + ")"); + } + + nodeService.setProperty(nodeRef, PROP_PUBLISH_IN_PROGRESS, false); + } + } + finally + { + behaviourFilter.enableBehaviour(nodeRef, TYPE_DISPOSITION_ACTION_DEFINITION); + } + + return null; + } + }; + retryingTransactionHelper.doInTransaction(execution); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/publish/BasePublishExecutor.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/publish/BasePublishExecutor.java new file mode 100644 index 0000000000..f216fd3701 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/publish/BasePublishExecutor.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2009-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.job.publish; + +/** + * Base publish executor implementation + * + * @author Roy Wetherall + */ +public abstract class BasePublishExecutor implements PublishExecutor +{ + /** Publish executor registry */ + public PublishExecutorRegistry registry; + + /** + * Set publish executor registry + * @param registry publish executor registry + */ + public void setPublishExecutorRegistry(PublishExecutorRegistry registry) + { + this.registry = registry; + } + + /** + * Init method + */ + public void init() + { + registry.register(this); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/publish/DispositionActionDefinitionPublishExecutor.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/publish/DispositionActionDefinitionPublishExecutor.java new file mode 100644 index 0000000000..79a0c3abfa --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/publish/DispositionActionDefinitionPublishExecutor.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2009-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.job.publish; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; +import org.alfresco.module.org_alfresco_module_rm.action.impl.BroadcastDispositionActionDefinitionUpdateAction; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.policy.BehaviourFilter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; + +/** + * Disposition action definition publish executor + * + * @author Roy Wetherall + */ +public class DispositionActionDefinitionPublishExecutor extends BasePublishExecutor +{ + /** Node service */ + private NodeService nodeService; + + /** Records management action service */ + private RecordsManagementActionService rmActionService; + + /** Behaviour filter */ + @SuppressWarnings("unused") + private BehaviourFilter behaviourFilter; + + /** + * Set node service + * @param nodeService node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Set records management service + * @param rmActionService records management service + */ + public void setRmActionService(RecordsManagementActionService rmActionService) + { + this.rmActionService = rmActionService; + } + + /** + * Set behaviour filter + * @param behaviourFilter behaviour filter + */ + public void setBehaviourFilter(BehaviourFilter behaviourFilter) + { + this.behaviourFilter = behaviourFilter; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.job.publish.PublishExecutor#getName() + */ + @Override + public String getName() + { + return RecordsManagementModel.UPDATE_TO_DISPOSITION_ACTION_DEFINITION; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.job.publish.PublishExecutor#publish(org.alfresco.service.cmr.repository.NodeRef) + */ + @SuppressWarnings("unchecked") + @Override + public void publish(NodeRef nodeRef) + { + List updatedProps = (List)nodeService.getProperty(nodeRef, RecordsManagementModel.PROP_UPDATED_PROPERTIES); + if (updatedProps != null) + { + Map params = new HashMap(); + params.put(BroadcastDispositionActionDefinitionUpdateAction.CHANGED_PROPERTIES, (Serializable)updatedProps); + rmActionService.executeRecordsManagementAction(nodeRef, BroadcastDispositionActionDefinitionUpdateAction.NAME, params); + } + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/publish/PublishExecutor.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/publish/PublishExecutor.java new file mode 100644 index 0000000000..e2496de335 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/publish/PublishExecutor.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2009-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.job.publish; + +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Publish executor interface. + * + * @author Roy Wetherall + */ +public interface PublishExecutor +{ + /** + * @return publish exector name + */ + String getName(); + + /** + * Publish changes to node. + * + * @param nodeRef node reference + */ + void publish(NodeRef nodeRef); +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/publish/PublishExecutorRegistry.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/publish/PublishExecutorRegistry.java new file mode 100644 index 0000000000..a109bd1046 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/publish/PublishExecutorRegistry.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2009-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.job.publish; + +import java.util.HashMap; +import java.util.Map; + +/** + * Publish executor register. + * + * @author Roy Wetherall + */ +public class PublishExecutorRegistry +{ + /** Map of publish executors */ + private Map publishExectors = new HashMap(3); + + /** + * Register a publish executor + * + * @param publishExecutor publish executor + */ + public void register(PublishExecutor publishExecutor) + { + publishExectors.put(publishExecutor.getName(), publishExecutor); + } + + /** + * Get registered publish executor by name. + * + * @param name name + * @return {@link PublishExecutor}] + */ + public PublishExecutor get(String name) + { + return publishExectors.get(name); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/ScriptCapability.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/ScriptCapability.java new file mode 100644 index 0000000000..dab8b72a80 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/ScriptCapability.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.jscript; + +/** + * @author Roy Wetherall + */ +public class ScriptCapability +{ + private String name; + private String displayLabel; + private String[] actions; + + /** + * @param name + * @param displayLabel + * @param actions + */ + protected ScriptCapability(String name, String displayLabel, String[] actions) + { + this.name = name; + this.displayLabel = displayLabel; + this.actions = actions; + } + + /** + * @return the name + */ + public String getName() + { + return name; + } + + /** + * @return the displayLabel + */ + public String getDisplayLabel() + { + return displayLabel; + } + + /** + * @return the actions + */ + public String[] getActions() + { + return actions; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/ScriptRecordsManagmentNode.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/ScriptRecordsManagmentNode.java new file mode 100644 index 0000000000..2ffb7cb2f8 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/ScriptRecordsManagmentNode.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.jscript; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry; +import org.alfresco.module.org_alfresco_module_rm.capability.Capability; +import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService; +import org.alfresco.repo.jscript.ScriptNode; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.AccessStatus; +import org.mozilla.javascript.Scriptable; + +/** + * Base records managment script node + * + * @author Roy Wetherall + */ +public class ScriptRecordsManagmentNode extends ScriptNode +{ + private static final long serialVersionUID = 8872385533440938353L; + + private RecordsManagementServiceRegistry rmServices; + + public ScriptRecordsManagmentNode(NodeRef nodeRef, RecordsManagementServiceRegistry services, Scriptable scope) + { + super(nodeRef, services, scope); + rmServices = services; + } + + public ScriptRecordsManagmentNode(NodeRef nodeRef, RecordsManagementServiceRegistry services) + { + super(nodeRef, services); + rmServices = services; + } + + public ScriptCapability[] getCapabilities() + { + return capabilitiesSet(null); + } + + public ScriptCapability[] capabilitiesSet(String capabilitiesSet) + { + RecordsManagementSecurityService rmSecurity = rmServices.getRecordsManagementSecurityService(); + Map cMap = null; + if (capabilitiesSet == null) + { + // Get all capabilities + cMap = rmSecurity.getCapabilities(this.nodeRef); + } + else + { + cMap = rmSecurity.getCapabilities(this.nodeRef, capabilitiesSet); + } + + List list = new ArrayList(cMap.size()); + for (Map.Entry entry : cMap.entrySet()) + { + if (AccessStatus.ALLOWED.equals(entry.getValue()) == true || + AccessStatus.UNDETERMINED.equals(entry.getValue()) == true) + { + Capability cap = entry.getKey(); + String[] actions = (String[])cap.getActionNames().toArray(new String[cap.getActionNames().size()]); + ScriptCapability scriptCap = new ScriptCapability(cap.getName(), cap.getName(), actions); + list.add(scriptCap); + } + } + + return (ScriptCapability[])list.toArray(new ScriptCapability[list.size()]); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/ScriptRecordsManagmentService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/ScriptRecordsManagmentService.java new file mode 100644 index 0000000000..c8515d457d --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/ScriptRecordsManagmentService.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.jscript; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.notification.RecordsManagementNotificationHelper; +import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService; +import org.alfresco.repo.jscript.BaseScopableProcessorExtension; +import org.alfresco.repo.jscript.ScriptNode; +import org.alfresco.scripts.ScriptException; + +/** + * Records management service + * + * @author Roy Wetherall + */ +public class ScriptRecordsManagmentService extends BaseScopableProcessorExtension + implements RecordsManagementModel +{ + /** Records management service registry */ + private RecordsManagementServiceRegistry rmServices; + + /** Records management notification helper */ + private RecordsManagementNotificationHelper notificationHelper; + + /** + * Set records management service registry + * + * @param rmServices records management service registry + */ + public void setRecordsManagementServiceRegistry(RecordsManagementServiceRegistry rmServices) + { + this.rmServices = rmServices; + } + + /** + * Sets the notification helper + * + * @param notificationHelper notification helper + */ + public void setNotificationHelper(RecordsManagementNotificationHelper notificationHelper) + { + this.notificationHelper = notificationHelper; + } + + /** + * Get records management node + * + * @param node script node + * @return ScriptRecordsManagementNode records management script node + */ + public ScriptRecordsManagmentNode getRecordsManagementNode(ScriptNode node) + { + ScriptRecordsManagmentNode result = null; + + if (rmServices.getNodeService().hasAspect(node.getNodeRef(), ASPECT_FILE_PLAN_COMPONENT) == true) + { + // TODO .. at this point determine what type of records management node is it and + // create the appropriate sub-type + result = new ScriptRecordsManagmentNode(node.getNodeRef(), rmServices); + } + else + { + throw new ScriptException("Node is not a records management node type."); + } + + return result; + } + + /** + * Set the RM permission + * + * @param node + * @param permission + * @param authority + */ + public void setPermission(ScriptNode node, String permission, String authority) + { + RecordsManagementSecurityService securityService = rmServices.getRecordsManagementSecurityService(); + securityService.setPermission(node.getNodeRef(), authority, permission); + } + + /** + * Delete the RM permission + * + * @param node + * @param permission + * @param authority + */ + public void deletePermission(ScriptNode node, String permission, String authority) + { + RecordsManagementSecurityService securityService = rmServices.getRecordsManagementSecurityService(); + securityService.deletePermission(node.getNodeRef(), authority, permission); + } + + /** + * Send superseded notification + * + * @param record superseded record + */ + public void sendSupersededNotification(ScriptNode record) + { + notificationHelper.recordSupersededEmailNotification(record.getNodeRef()); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/BaseEvaluator.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/BaseEvaluator.java new file mode 100644 index 0000000000..014f4ad8e5 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/BaseEvaluator.java @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.jscript.app; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.module.org_alfresco_module_rm.FilePlanComponentKind; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.capability.Capability; +import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.namespace.NamespaceService; + +/** + * Base evaluator. + * + * @author Roy Wetherall + */ +public abstract class BaseEvaluator implements RecordsManagementModel +{ + /** Name */ + protected String name; + + /** JSON conversion component */ + protected JSONConversionComponent jsonConversionComponent; + + /** Records management service */ + protected RecordsManagementService recordsManagementService; + + /** Node service */ + protected NodeService nodeService; + + /** Namespace service */ + protected NamespaceService namespaceService; + + /** Capability service */ + protected CapabilityService capabilityService; + + /** File plan component kinds */ + protected Set kinds; + + /** Capabilities */ + protected List capabilities; + + /** + * @param jsonConversionComponent json conversion component + */ + public void setJsonConversionComponent(JSONConversionComponent jsonConversionComponent) + { + this.jsonConversionComponent = jsonConversionComponent; + } + + /** + * @param recordsManagementService records management service + */ + public void setRecordsManagementService(RecordsManagementService recordsManagementService) + { + this.recordsManagementService = recordsManagementService; + } + + /** + * @param nodeService node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @param namespaceService namespace service + */ + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + /** + * @param capabilityService capability service + */ + public void setCapabilityService(CapabilityService capabilityService) + { + this.capabilityService = capabilityService; + } + + /** + * @param name + */ + public void setName(String name) + { + this.name = name; + } + + /** + * @return + */ + public String getName() + { + return this.name; + } + + /** + * @param kinds + */ + public void setKinds(Set kinds) + { + this.kinds = kinds; + } + + /** + * @param capabilties + */ + public void setCapabilities(List capabilties) + { + this.capabilities = capabilties; + } + + /** + * Helper method which sets on capability. + * + * @param capability capability name + */ + public void setCapability(String capability) + { + List list = new ArrayList(1); + list.add(capability); + this.capabilities = list; + } + + /** + * Registers this instance as an indicator (evaluator) + */ + public void registerIndicator() + { + jsonConversionComponent.registerIndicator(this); + } + + /** + * Registers this instance as an action (evaluator) + */ + public void registerAction() + { + jsonConversionComponent.registerAction(this); + } + + /** + * Executes the evaluation. + * + * @param nodeRef + * @return + */ + public boolean evaluate(NodeRef nodeRef) + { + boolean result = false; + + // Check that we are dealing with the correct kind of RM object + if (kinds == null || checkKinds(nodeRef) == true) + { + // Check we have the required capabilities + if (capabilities == null || checkCapabilities(nodeRef) == true) + { + result = evaluateImpl(nodeRef); + } + } + + return result; + } + + /** + * Checks the file plan component kind. + * + * @param nodeRef + * @return + */ + private boolean checkKinds(NodeRef nodeRef) + { + FilePlanComponentKind kind = recordsManagementService.getFilePlanComponentKind(nodeRef); + return kinds.contains(kind); + } + + /** + * Checks the capabilities. + * + * @param nodeRef + * @return + */ + private boolean checkCapabilities(NodeRef nodeRef) + { + boolean result = true; + if (capabilities != null && capabilities.isEmpty() == false) + { + Map accessStatus = capabilityService.getCapabilitiesAccessState(nodeRef, capabilities); + for (AccessStatus value : accessStatus.values()) + { + if (AccessStatus.DENIED.equals(value) == true) + { + result = false; + break; + } + } + } + return result; + } + + /** + * Evaluation execution implementation. + * + * @param nodeRef + * @return + */ + protected abstract boolean evaluateImpl(NodeRef nodeRef); +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/JSONConversionComponent.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/JSONConversionComponent.java new file mode 100644 index 0000000000..ee8ca3dcfb --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/JSONConversionComponent.java @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.jscript.app; + +import java.util.ArrayList; +import java.util.List; + +import org.alfresco.module.org_alfresco_module_rm.FilePlanComponentKind; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; +import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.AccessStatus; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +/** + * Extend JSON conversion component to include RM specifics. + * + * @author Roy Wetherall + */ +public class JSONConversionComponent extends org.alfresco.repo.jscript.app.JSONConversionComponent +{ + /** Records management service */ + private RecordsManagementService recordsManagementService; + + /** Indicators */ + private List indicators = new ArrayList(); + + /** Actions */ + private List actions = new ArrayList(); + + /** + * @param recordsManagementService records management service + */ + public void setRecordsManagementService(RecordsManagementService recordsManagementService) + { + this.recordsManagementService = recordsManagementService; + } + + /** + * @param indicator registered indicator + */ + public void registerIndicator(BaseEvaluator indicator) + { + indicators.add(indicator); + } + + /** + * @param action registered action + */ + public void registerAction(BaseEvaluator action) + { + actions.add(action); + } + + /** + * @see org.alfresco.repo.jscript.app.JSONConversionComponent#setRootValues(org.alfresco.service.cmr.model.FileInfo, org.json.simple.JSONObject, boolean) + */ + @SuppressWarnings("unchecked") + @Override + protected void setRootValues(FileInfo nodeInfo, JSONObject rootJSONObject, boolean useShortQNames) + { + // Set the base root values + super.setRootValues(nodeInfo, rootJSONObject, useShortQNames); + + // Get the node reference for convenience + NodeRef nodeRef = nodeInfo.getNodeRef(); + + if (AccessStatus.ALLOWED.equals(permissionService.hasPermission(nodeRef, RMPermissionModel.READ_RECORDS)) == true) + { + // Indicate whether the node is a RM object or not + boolean isFilePlanComponent = recordsManagementService.isFilePlanComponent(nodeInfo.getNodeRef()); + rootJSONObject.put("isRmNode", isFilePlanComponent); + + if (isFilePlanComponent == true) + { + rootJSONObject.put("rmNode", setRmNodeValues(nodeRef, rootJSONObject, useShortQNames)); + } + } + } + + /** + * + * @param nodeRef + * @param rootJSONObject + * @param useShortQName + * @return + */ + @SuppressWarnings("unchecked") + private JSONObject setRmNodeValues(NodeRef nodeRef, JSONObject rootJSONObject, boolean useShortQName) + { + JSONObject rmNodeValues = new JSONObject(); + + // UI convenience type + rmNodeValues.put("uiType", getUIType(nodeRef)); + + // Get the 'kind' of the file plan component + FilePlanComponentKind kind = recordsManagementService.getFilePlanComponentKind(nodeRef); + rmNodeValues.put("kind", kind.toString()); + + // File plan node reference + rmNodeValues.put("filePlan", recordsManagementService.getFilePlan(nodeRef).toString()); + + // Set the indicators array + setIndicators(rmNodeValues, nodeRef); + + // Set the actions array + setActions(rmNodeValues, nodeRef); + + return rmNodeValues; + } + + @SuppressWarnings("unchecked") + private void setIndicators(JSONObject rmNodeValues, NodeRef nodeRef) + { + if (indicators != null && indicators.isEmpty() == false) + { + JSONArray jsonIndicators = new JSONArray(); + + for (BaseEvaluator indicator : indicators) + { + if (indicator.evaluate(nodeRef) == true) + { + jsonIndicators.add(indicator.getName()); + } + } + + rmNodeValues.put("indicators", jsonIndicators); + } + } + + @SuppressWarnings("unchecked") + private void setActions(JSONObject rmNodeValues, NodeRef nodeRef) + { + if (actions != null && actions.isEmpty() == false) + { + JSONArray jsonActions = new JSONArray(); + + for (BaseEvaluator action : actions) + { + if (action.evaluate(nodeRef) == true) + { + jsonActions.add(action.getName()); + } + } + + rmNodeValues.put("actions", jsonActions); + } + } + + /** + * Gets the rm 'type' used as a UI convenience and compatibility flag. + */ + private String getUIType(NodeRef nodeRef) + { + String result = "unknown"; + + FilePlanComponentKind kind = recordsManagementService.getFilePlanComponentKind(nodeRef); + if (kind != null) + { + switch (kind) + { + case FILE_PLAN: + { + result = "fileplan"; + break; + } + case RECORD_CATEGORY: + { + result = "record-category"; + break; + } + case RECORD_FOLDER: + { + if (recordsManagementService.isMetadataStub(nodeRef) == true) + { + result = "metadata-stub-folder"; + } + else + { + result = "record-folder"; + } + break; + } + case RECORD: + { + if (recordsManagementService.isMetadataStub(nodeRef) == true) + { + result = "metadata-stub"; + } + else + { + if (recordsManagementService.isRecordDeclared(nodeRef) == true) + { + result = "record"; + } + else + { + result = "undeclared-record"; + } + } + break; + } + case HOLD: + { + result = "hold-container"; + break; + } + case TRANSFER: + { + result = "transfer-container"; + break; + } + } + } + + return result; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/CutoffEvaluator.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/CutoffEvaluator.java new file mode 100644 index 0000000000..0733bf7e16 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/CutoffEvaluator.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.jscript.app.evaluator; + +import org.alfresco.module.org_alfresco_module_rm.jscript.app.BaseEvaluator; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Cutoff indicator + * + * @author Roy Wetherall + */ +public class CutoffEvaluator extends BaseEvaluator +{ + private boolean isCutoff = true; + + public void setCutoff(boolean isCutoff) + { + this.isCutoff = isCutoff; + } + + @Override + protected boolean evaluateImpl(NodeRef nodeRef) + { + return (recordsManagementService.isCutoff(nodeRef) == isCutoff); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/FolderOpenClosedEvaluator.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/FolderOpenClosedEvaluator.java new file mode 100644 index 0000000000..7855ee3b89 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/FolderOpenClosedEvaluator.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.jscript.app.evaluator; + +import org.alfresco.module.org_alfresco_module_rm.jscript.app.BaseEvaluator; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * @author Roy Wetherall + */ +public class FolderOpenClosedEvaluator extends BaseEvaluator +{ + private boolean expected = true; + + public void setExpected(boolean expected) + { + this.expected = expected; + } + + @Override + protected boolean evaluateImpl(NodeRef nodeRef) + { + return (recordsManagementService.isRecordFolderClosed(nodeRef) == expected); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/FrozenEvaluator.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/FrozenEvaluator.java new file mode 100644 index 0000000000..3159c746b0 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/FrozenEvaluator.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.jscript.app.evaluator; + +import org.alfresco.module.org_alfresco_module_rm.jscript.app.BaseEvaluator; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * @author Roy Wetherall + */ +public class FrozenEvaluator extends BaseEvaluator +{ + @Override + protected boolean evaluateImpl(NodeRef nodeRef) + { + return recordsManagementService.isFrozen(nodeRef); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/HasAspectEvaluator.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/HasAspectEvaluator.java new file mode 100644 index 0000000000..c95f793e9f --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/HasAspectEvaluator.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.jscript.app.evaluator; + +import org.alfresco.module.org_alfresco_module_rm.jscript.app.BaseEvaluator; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * Transfered indicator + * + * @author Roy Wetherall + */ +public class HasAspectEvaluator extends BaseEvaluator +{ + private String prefixAspectQNameString; + + public void setAspect(String aspect) + { + prefixAspectQNameString = aspect; + } + + @Override + protected boolean evaluateImpl(NodeRef nodeRef) + { + QName aspect = QName.createQName(prefixAspectQNameString, namespaceService); + return nodeService.hasAspect(nodeRef, aspect); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/MultiParentEvaluator.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/MultiParentEvaluator.java new file mode 100644 index 0000000000..805a082711 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/MultiParentEvaluator.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.jscript.app.evaluator; + +import java.util.List; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.jscript.app.BaseEvaluator; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.RegexQNamePattern; + +/** + * @author Roy Wetherall + */ +public class MultiParentEvaluator extends BaseEvaluator +{ + @Override + protected boolean evaluateImpl(NodeRef nodeRef) + { + List parents = nodeService.getParentAssocs(nodeRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + return (parents.size() > 1); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/NonElectronicEvaluator.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/NonElectronicEvaluator.java new file mode 100644 index 0000000000..2c88c5d0ac --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/NonElectronicEvaluator.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.jscript.app.evaluator; + +import org.alfresco.module.org_alfresco_module_rm.jscript.app.BaseEvaluator; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * @author Roy Wetherall + */ +public class NonElectronicEvaluator extends BaseEvaluator +{ + private DictionaryService dictionaryService; + + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + @Override + protected boolean evaluateImpl(NodeRef nodeRef) + { + boolean result = false; + QName qName = nodeService.getType(nodeRef); + if (qName != null && dictionaryService.isSubClass(qName, TYPE_NON_ELECTRONIC_DOCUMENT) == true) + { + result = true; + } + return result; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/SplitEmailActionEvaluator.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/SplitEmailActionEvaluator.java new file mode 100644 index 0000000000..c28f1ae23b --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/SplitEmailActionEvaluator.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.jscript.app.evaluator; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.jscript.app.BaseEvaluator; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Split EMail action evaluator + * + * @author Roy Wetherall + */ +public class SplitEmailActionEvaluator extends BaseEvaluator +{ + @Override + protected boolean evaluateImpl(NodeRef nodeRef) + { + boolean result = false; + if (recordsManagementService.isRecordDeclared(nodeRef) == false) + { + ContentData contentData = (ContentData)nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT); + if (contentData != null) + { + String mimetype = contentData.getMimetype(); + if (mimetype != null && + (MimetypeMap.MIMETYPE_RFC822.equals(mimetype) == true || + MimetypeMap.MIMETYPE_OUTLOOK_MSG.equals(mimetype) == true)) + { + result = true; + } + } + } + return result; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/TransferEvaluator.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/TransferEvaluator.java new file mode 100644 index 0000000000..8a22f8085a --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/TransferEvaluator.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.jscript.app.evaluator; + +import java.util.List; + +import org.alfresco.module.org_alfresco_module_rm.jscript.app.BaseEvaluator; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.RegexQNamePattern; + +/** + * @author Roy Wetherall + */ +public class TransferEvaluator extends BaseEvaluator +{ + private boolean transferAccessionIndicator = false; + + public void setTransferAccessionIndicator(boolean transferAccessionIndicator) + { + this.transferAccessionIndicator = transferAccessionIndicator; + } + + @Override + protected boolean evaluateImpl(NodeRef nodeRef) + { + List parents = nodeService.getParentAssocs(nodeRef, RecordsManagementModel.ASSOC_TRANSFERRED, RegexQNamePattern.MATCH_ALL); + if (parents.size() == 1) + { + boolean actual = ((Boolean)nodeService.getProperty(parents.get(0).getParentRef(), RecordsManagementModel.PROP_TRANSFER_ACCESSION_INDICATOR)).booleanValue(); + return (actual == transferAccessionIndicator); + } + else + { + return false; + } + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/TrueEvaluator.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/TrueEvaluator.java new file mode 100644 index 0000000000..e559216b3b --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/TrueEvaluator.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.jscript.app.evaluator; + +import org.alfresco.module.org_alfresco_module_rm.jscript.app.BaseEvaluator; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Cutoff indicator + * + * @author Roy Wetherall + */ +public class TrueEvaluator extends BaseEvaluator +{ + @Override + protected boolean evaluateImpl(NodeRef nodeRef) + { + return true; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/VitalRecordEvaluator.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/VitalRecordEvaluator.java new file mode 100644 index 0000000000..65c253976a --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/VitalRecordEvaluator.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.jscript.app.evaluator; + +import org.alfresco.module.org_alfresco_module_rm.jscript.app.BaseEvaluator; +import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordService; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * @author Roy Wetherall + */ +public class VitalRecordEvaluator extends BaseEvaluator +{ + private VitalRecordService vitalRecordService; + + public void setVitalRecordService(VitalRecordService vitalRecordService) + { + this.vitalRecordService = vitalRecordService; + } + + @Override + protected boolean evaluateImpl(NodeRef nodeRef) + { + return vitalRecordService.isVitalRecord(nodeRef); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/CustomisableTypesBootstrap.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/CustomisableTypesBootstrap.java new file mode 100644 index 0000000000..d9f6e5722b --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/CustomisableTypesBootstrap.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.model; + +import java.util.List; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * Bootstrap bean that indicates that the specified types or aspects are + * + * @author Roy Wetherall + */ +public class CustomisableTypesBootstrap +{ + /** Records management admin service */ + private RecordsManagementAdminService recordsManagementAdminService; + + /** Namespace service */ + private NamespaceService namespaceService; + + /** List of types and aspects to register as customisable */ + private List customisable; + + /** + * @param recordsManagementAdminService records management admin service + */ + public void setRecordsManagementAdminService(RecordsManagementAdminService recordsManagementAdminService) + { + this.recordsManagementAdminService = recordsManagementAdminService; + } + + /** + * @param namespaceService namespace service + */ + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + /** + * @param customizable list of types and aspects to register as customisable + */ + public void setCustomisable(List customisable) + { + this.customisable = customisable; + } + + /** + * Bean initialisation method + */ + public void init() + { + for (String customType : customisable) + { + QName customTypeQName = QName.createQName(customType, namespaceService); + recordsManagementAdminService.makeCustomisable(customTypeQName); + } + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/FilePlanComponentAspect.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/FilePlanComponentAspect.java new file mode 100644 index 0000000000..637f0caa4d --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/FilePlanComponentAspect.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.model; + +import java.io.Serializable; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.repo.copy.AbstractCopyBehaviourCallback; +import org.alfresco.repo.copy.CopyBehaviourCallback; +import org.alfresco.repo.copy.CopyDetails; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.policy.Behaviour.NotificationFrequency; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * Behaviour associated with the file plan component aspect + * + * @author Roy Wetherall + */ +public class FilePlanComponentAspect implements RecordsManagementModel, + NodeServicePolicies.OnAddAspectPolicy, + NodeServicePolicies.OnMoveNodePolicy +{ + /** Policy component */ + private PolicyComponent policyComponent; + + /** Records Management Service */ + private RecordsManagementService recordsManagementService; + + /** Node service */ + private NodeService nodeService; + + /** + * Set the policy component + * @param policyComponent policy component + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * Set the records management service + * @param recordsManagementService records management service + */ + public void setRecordsManagementService(RecordsManagementService recordsManagementService) + { + this.recordsManagementService = recordsManagementService; + } + + /** + * Set node service + * @param nodeService node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Bean initialisation method + */ + public void init() + { + policyComponent.bindClassBehaviour( + NodeServicePolicies.OnAddAspectPolicy.QNAME, + ASPECT_FILE_PLAN_COMPONENT, + new JavaBehaviour(this, "onAddAspect", NotificationFrequency.TRANSACTION_COMMIT)); + policyComponent.bindClassBehaviour( + NodeServicePolicies.OnMoveNodePolicy.QNAME, + ASPECT_FILE_PLAN_COMPONENT, + new JavaBehaviour(this, "onMoveNode", NotificationFrequency.TRANSACTION_COMMIT)); + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"), + ASPECT_FILE_PLAN_COMPONENT, + new JavaBehaviour(this, "getCopyCallback")); + } + + /** + * @see org.alfresco.repo.node.NodeServicePolicies.OnAddAspectPolicy#onAddAspect(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + @Override + public void onAddAspect(final NodeRef nodeRef, final QName aspectTypeQName) + { + AuthenticationUtil.runAs(new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + if (nodeService.exists(nodeRef) == true) + { + // Look up the root and set on the aspect if found + NodeRef root = recordsManagementService.getFilePlan(nodeRef); + if (root != null) + { + nodeService.setProperty(nodeRef, PROP_ROOT_NODEREF, root); + } + } + + return null; + } + }, AuthenticationUtil.getSystemUserName()); + } + + /** + * @see org.alfresco.repo.node.NodeServicePolicies.OnMoveNodePolicy#onMoveNode(org.alfresco.service.cmr.repository.ChildAssociationRef, org.alfresco.service.cmr.repository.ChildAssociationRef) + */ + @Override + public void onMoveNode(final ChildAssociationRef oldChildAssocRef, final ChildAssociationRef newChildAssocRef) + { + AuthenticationUtil.runAs(new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + if (nodeService.exists(newChildAssocRef.getParentRef()) == true && + nodeService.exists(newChildAssocRef.getChildRef()) == true) + { + // Look up the root and re-set the value currently stored on the aspect + NodeRef root = recordsManagementService.getFilePlan(newChildAssocRef.getParentRef()); + // NOTE: set the null value if no root found + nodeService.setProperty(newChildAssocRef.getChildRef(), PROP_ROOT_NODEREF, root); + } + + return null; + } + }, AuthenticationUtil.getSystemUserName()); + } + + /** + * Copy behaviour call back + * + * @param classRef class reference + * @param copyDetail details of the information being copied + * @return CopyBehaviourCallback + */ + public CopyBehaviourCallback getCopyCallback(QName classRef, CopyDetails copyDetails) + { + return new AbstractCopyBehaviourCallback() + { + /** + * @see org.alfresco.repo.copy.CopyBehaviourCallback#getChildAssociationCopyAction(org.alfresco.service.namespace.QName, org.alfresco.repo.copy.CopyDetails, org.alfresco.repo.copy.CopyBehaviourCallback.CopyChildAssociationDetails) + */ + public ChildAssocCopyAction getChildAssociationCopyAction( + QName classQName, + CopyDetails copyDetails, + CopyChildAssociationDetails childAssocCopyDetails) + { + // Do not copy the associations + return null; + } + + /** + * @see org.alfresco.repo.copy.CopyBehaviourCallback#getCopyProperties(org.alfresco.service.namespace.QName, org.alfresco.repo.copy.CopyDetails, java.util.Map) + */ + public Map getCopyProperties( + QName classQName, + CopyDetails copyDetails, + Map properties) + { + // Only copy the root node reference if the new value can be looked up via the parent + NodeRef root = recordsManagementService.getFilePlan(copyDetails.getTargetParentNodeRef()); + if (root != null) + { + properties.put(PROP_ROOT_NODEREF, root); + } + return properties; + } + + /** + * @see org.alfresco.repo.copy.CopyBehaviourCallback#getMustCopy(org.alfresco.service.namespace.QName, org.alfresco.repo.copy.CopyDetails) + */ + public boolean getMustCopy(QName classQName, CopyDetails copyDetails) + { + // Ensure the aspect is copied + return true; + } + }; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordComponentIdentifierAspect.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordComponentIdentifierAspect.java new file mode 100644 index 0000000000..d2589821b2 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordComponentIdentifierAspect.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.model; + +import java.io.Serializable; +import java.util.Map; + +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy; +import org.alfresco.repo.node.NodeServicePolicies.OnUpdatePropertiesPolicy; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.policy.Behaviour.NotificationFrequency; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.service.cmr.attributes.AttributeService; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.PropertyCheck; + +/** + * Record component identifier aspect behaviour + * + * @author Roy Wetherall + */ +public class RecordComponentIdentifierAspect + implements NodeServicePolicies.OnUpdatePropertiesPolicy, + NodeServicePolicies.BeforeDeleteNodePolicy, + RecordsManagementModel +{ + private static final String CONTEXT_VALUE = "rma:identifier"; + + private PolicyComponent policyComponent; + private NodeService nodeService; + private AttributeService attributeService; + + /** + * @param policyComponent the policyComponent to set + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * @param nodeService the nodeService to set + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Set the component to manage the unique properties + */ + public void setAttributeService(AttributeService attributeService) + { + this.attributeService = attributeService; + } + + /** + * Initialise method + */ + public void init() + { + PropertyCheck.mandatory(this, "policyComponent", policyComponent); + PropertyCheck.mandatory(this, "nodeService", nodeService); + PropertyCheck.mandatory(this, "attributeService", attributeService); + + policyComponent.bindClassBehaviour( + OnUpdatePropertiesPolicy.QNAME, + ASPECT_RECORD_COMPONENT_ID, + new JavaBehaviour(this, "onUpdateProperties", NotificationFrequency.EVERY_EVENT)); + policyComponent.bindClassBehaviour( + BeforeDeleteNodePolicy.QNAME, + ASPECT_RECORD_COMPONENT_ID, + new JavaBehaviour(this, "beforeDeleteNode", NotificationFrequency.EVERY_EVENT)); + } + + /** + * Ensures that the {@link RecordsManagementModel#PROP_IDENTIFIER rma:identifier} property remains + * unique within the context of the parent node. + */ + public void onUpdateProperties(final NodeRef nodeRef, final Map before, final Map after) + { + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() + { + // Check whether the identifier property has changed + String beforeId = (String)before.get(PROP_IDENTIFIER); + String afterId = (String)after.get(PROP_IDENTIFIER); + updateUniqueness(nodeRef, beforeId, afterId); + return null; + } + }, AuthenticationUtil.getSystemUserName()); + } + + /** + * Cleans up the {@link RecordsManagementModel#PROP_IDENTIFIER rma:identifier} property unique triplet. + */ + public void beforeDeleteNode(final NodeRef nodeRef) + { + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() + { + String beforeId = (String) nodeService.getProperty(nodeRef, PROP_IDENTIFIER); + updateUniqueness(nodeRef, beforeId, null); + return null; + } + }, AuthenticationUtil.getSystemUserName()); + } + + /** + * Updates the uniqueness check using the values provided. If the after value is null + * then this is considered to be a removal. + */ + private void updateUniqueness(NodeRef nodeRef, String beforeId, String afterId) + { + ChildAssociationRef childAssoc = nodeService.getPrimaryParent(nodeRef); + NodeRef contextNodeRef = childAssoc.getParentRef(); + + if (beforeId == null) + { + if (afterId != null) + { + // Just create it + attributeService.createAttribute(null, CONTEXT_VALUE, contextNodeRef, afterId); + } + else + { + // This happens if the unique property is not present + } + } + else if (afterId == null) + { + if (beforeId != null) + { + // The before value was not null, so remove it + attributeService.removeAttribute(CONTEXT_VALUE, contextNodeRef, beforeId); + } + // Do a blanket removal in case this is a contextual nodes + attributeService.removeAttributes(CONTEXT_VALUE, nodeRef); + } + else + { + // This is a full update + attributeService.updateOrCreateAttribute( + CONTEXT_VALUE, contextNodeRef, beforeId, + CONTEXT_VALUE, contextNodeRef, afterId); + } + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordContainerType.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordContainerType.java new file mode 100644 index 0000000000..5bd85b7f1e --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordContainerType.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.model; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; +import org.alfresco.module.org_alfresco_module_rm.identifier.IdentifierService; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.policy.Behaviour.NotificationFrequency; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * Behaviour associated with the record container type + * + * @author Roy Wetherall + */ +public class RecordContainerType implements RecordsManagementModel, + NodeServicePolicies.OnCreateChildAssociationPolicy, + NodeServicePolicies.OnCreateNodePolicy +{ + /** Policy component */ + private PolicyComponent policyComponent; + + /** Records Management Action Service */ + private RecordsManagementActionService recordsManagementActionService; + + /** Node service */ + private NodeService nodeService; + + /** Dictionary service */ + private DictionaryService dictionaryService; + + /** Identity service */ + private IdentifierService recordsManagementIdentifierService; + + /** + * Set the policy component + * @param policyComponent policy component + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * Set the records management action service + * + * @param recordsManagementActionService records management action service + */ + public void setRecordsManagementActionService(RecordsManagementActionService recordsManagementActionService) + { + this.recordsManagementActionService = recordsManagementActionService; + } + + /** + * Set node service + * + * @param nodeService node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Set dictionary service + * + * @param dictionaryService dictionary serviceS + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + /** + * Set the identity service + * + * @param recordsManagementIdentifierService identity service + */ + public void setRecordsManagementIdentifierService(IdentifierService recordsManagementIdentifierService) + { + this.recordsManagementIdentifierService = recordsManagementIdentifierService; + } + + /** + * Bean initialisation method + */ + public void init() + { + this.policyComponent.bindAssociationBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateChildAssociation"), + TYPE_RECORDS_MANAGEMENT_CONTAINER, + ContentModel.ASSOC_CONTAINS, + new JavaBehaviour(this, "onCreateChildAssociation", NotificationFrequency.TRANSACTION_COMMIT)); + this.policyComponent.bindClassBehaviour( + NodeServicePolicies.OnCreateNodePolicy.QNAME, + TYPE_FILE_PLAN, + new JavaBehaviour(this, "onCreateNode", NotificationFrequency.TRANSACTION_COMMIT)); + } + + /** + * Deal with something created within a record container + */ + @Override + public void onCreateChildAssociation(ChildAssociationRef childAssocRef, boolean isNewNode) + { + // Get the elements of the created association + final NodeRef child = childAssocRef.getChildRef(); + QName childType = nodeService.getType(child); + + // We only care about "folder" or sub-types + if (dictionaryService.isSubClass(childType, ContentModel.TYPE_FOLDER) == true) + { + // We need to automatically cast the created folder to RM type if it is a plain folder + // This occurs if the RM folder has been created via IMap, WebDav, etc + if (nodeService.hasAspect(child, ASPECT_FILE_PLAN_COMPONENT) == false) + { + // TODO it may not always be a record folder ... perhaps if the current user is a admin it would be a record category?? + + // Assume any created folder is a rma:recordFolder + nodeService.setType(child, TYPE_RECORD_FOLDER); + } + + if (TYPE_RECORD_FOLDER.equals(nodeService.getType(child)) == true) + { + // Setup record folder + recordsManagementActionService.executeRecordsManagementAction(child, "setupRecordFolder"); + } + + // Catch all to generate the rm id (assuming it doesn't already have one!) + setIdenifierProperty(child); + + } + } + + /** + * @see org.alfresco.repo.node.NodeServicePolicies.OnCreateNodePolicy#onCreateNode(org.alfresco.service.cmr.repository.ChildAssociationRef) + */ + @Override + public void onCreateNode(ChildAssociationRef childAssocRef) + { + // When a new root container is created, make sure the identifier is set + setIdenifierProperty(childAssocRef.getChildRef()); + } + + /** + * + * @param nodeRef + */ + private void setIdenifierProperty(final NodeRef nodeRef) + { + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() throws Exception + { + if (nodeService.hasAspect(nodeRef, ASPECT_FILE_PLAN_COMPONENT) == true && + nodeService.getProperty(nodeRef, PROP_IDENTIFIER) == null) + { + String id = recordsManagementIdentifierService.generateIdentifier(nodeRef); + nodeService.setProperty(nodeRef, RecordsManagementModel.PROP_IDENTIFIER, id); + } + return null; + } + }, AuthenticationUtil.getSystemUserName()); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordCopyBehaviours.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordCopyBehaviours.java new file mode 100644 index 0000000000..7c4377c9d0 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordCopyBehaviours.java @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.model; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; +import org.alfresco.repo.copy.AbstractCopyBehaviourCallback; +import org.alfresco.repo.copy.CopyBehaviourCallback; +import org.alfresco.repo.copy.CopyDetails; +import org.alfresco.repo.copy.DoNothingCopyBehaviourCallback; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.policy.Behaviour.NotificationFrequency; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * Class containing behaviour for the vitalRecordDefinition aspect. + * + * @author neilm + */ +public class RecordCopyBehaviours implements RecordsManagementModel +{ + /** The policy component */ + private PolicyComponent policyComponent; + + /** The rm service registry */ + private RecordsManagementServiceRegistry rmServiceRegistry; + + /** List of aspects to remove during move and copy */ + private List unwantedAspects = new ArrayList(5); + + /** + * Set the policy component + * + * @param policyComponent the policy component + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * Set the rm service registry. + * + * @param recordsManagementServiceRegistry the rm service registry. + */ + public void setRecordsManagementServiceRegistry(RecordsManagementServiceRegistry recordsManagementServiceRegistry) + { + this.rmServiceRegistry = recordsManagementServiceRegistry; + } + + /** + * Initialise the vitalRecord aspect policies + */ + public void init() + { + // Set up list of unwanted aspects + unwantedAspects.add(ASPECT_VITAL_RECORD); + unwantedAspects.add(ASPECT_DISPOSITION_LIFECYCLE); + unwantedAspects.add(RecordsManagementSearchBehaviour.ASPECT_RM_SEARCH); + + // Do not copy any of the Alfresco-internal 'state' aspects + for (QName aspect : unwantedAspects) + { + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"), + aspect, + new JavaBehaviour(this, "getDoNothingCopyCallback")); + } + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"), + ASPECT_RECORD_COMPONENT_ID, + new JavaBehaviour(this, "getIdCallback")); + + // Move behaviour + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onMoveNode"), + RecordsManagementModel.ASPECT_RECORD, + new JavaBehaviour(this, "onMoveRecordNode", NotificationFrequency.FIRST_EVENT)); + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onMoveNode"), + RecordsManagementModel.TYPE_RECORD_FOLDER, + new JavaBehaviour(this, "onMoveRecordFolderNode", NotificationFrequency.FIRST_EVENT)); + } + + /** + * onMove record behaviour + * + * @param oldChildAssocRef + * @param newChildAssocRef + */ + public void onMoveRecordNode(ChildAssociationRef oldChildAssocRef, ChildAssociationRef newChildAssocRef) + { + final NodeRef newNodeRef = newChildAssocRef.getChildRef(); + final NodeService nodeService = rmServiceRegistry.getNodeService(); + + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Object doWork() throws Exception + { + if (nodeService.exists(newNodeRef) == true) + { + // Remove unwanted aspects + removeUnwantedAspects(nodeService, newNodeRef); + } + + return null; + } + }, AuthenticationUtil.getAdminUserName()); + } + + /** + * onMove record folder behaviour + * + * @param oldChildAssocRef + * @param newChildAssocRef + */ + public void onMoveRecordFolderNode(ChildAssociationRef oldChildAssocRef, ChildAssociationRef newChildAssocRef) + { + final NodeRef newNodeRef = newChildAssocRef.getChildRef(); + final NodeService nodeService = rmServiceRegistry.getNodeService(); + final RecordsManagementService rmService = rmServiceRegistry.getRecordsManagementService(); + final RecordsManagementActionService rmActionService = rmServiceRegistry.getRecordsManagementActionService(); + + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Object doWork() throws Exception + { + if (nodeService.exists(newNodeRef) == true) + { + // Remove unwanted aspects + removeUnwantedAspects(nodeService, newNodeRef); + + // Trigger folder setup + rmActionService.executeRecordsManagementAction(newNodeRef, "setupRecordFolder"); + + // Sort out the child records + for (NodeRef record : rmService.getRecords(newNodeRef)) + { + removeUnwantedAspects(nodeService, record); + rmActionService.executeRecordsManagementAction(record, "file"); + } + } + + return null; + } + }, AuthenticationUtil.getAdminUserName()); + } + + /** + * Removes unwanted aspects + * + * @param nodeService + * @param nodeRef + */ + private void removeUnwantedAspects(NodeService nodeService, NodeRef nodeRef) + { + // Remove unwanted aspects + for (QName aspect : unwantedAspects) + { + if (nodeService.hasAspect(nodeRef, aspect) == true) + { + nodeService.removeAspect(nodeRef, aspect); + } + } + } + + /** + * Get the "do nothing" call back behaviour + * + * @param classRef + * @param copyDetails + * @return + */ + public CopyBehaviourCallback getDoNothingCopyCallback(QName classRef, CopyDetails copyDetails) + { + return new DoNothingCopyBehaviourCallback(); + } + + public CopyBehaviourCallback getIdCallback(QName classRef, CopyDetails copyDetails) + { + return new AbstractCopyBehaviourCallback() + { + public ChildAssocCopyAction getChildAssociationCopyAction( + QName classQName, + CopyDetails copyDetails, + CopyChildAssociationDetails childAssocCopyDetails) + { + return null; + } + + public Map getCopyProperties( + QName classQName, + CopyDetails copyDetails, + Map properties) + { + properties.put(PROP_IDENTIFIER, properties.get(PROP_IDENTIFIER) + "1"); + return properties; + } + + public boolean getMustCopy(QName classQName, CopyDetails copyDetails) + { + return true; + } + + }; + } + + /** + * Function to pad a string with zero '0' characters to the required length + * + * @param s String to pad with leading zero '0' characters + * @param len Length to pad to + * + * @return padded string or the original if already at >=len characters + */ + protected String padString(String s, int len) + { + String result = s; + for (int i=0; i<(len - s.length()); i++) + { + result = "0" + result; + } + return result; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementCustomModel.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementCustomModel.java new file mode 100644 index 0000000000..8751493a3a --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementCustomModel.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.model; + +import org.alfresco.service.namespace.QName; + +/** + * Helper class containing records management custom model qualified names + * + * @author Gavin Cornwell + */ +public interface RecordsManagementCustomModel +{ + // Namespace details + public static String RM_CUSTOM_URI = "http://www.alfresco.org/model/rmcustom/1.0"; + public static String RM_CUSTOM_PREFIX = "rmc"; + + // Model + public static QName RM_CUSTOM_MODEL = QName.createQName(RM_CUSTOM_URI, "rmcustom"); + + // Custom constraint for Supplemental Marking List + public static QName CONSTRAINT_CUSTOM_SMLIST = QName.createQName(RM_CUSTOM_URI, "smList"); + + // Custom property for for Supplemental Marking List + public static QName PROP_SUPPLEMENTAL_MARKING_LIST = QName.createQName(RM_CUSTOM_URI, "supplementalMarkingList"); + + // Supplemental Marking List aspect + public static QName ASPECT_SUPPLEMENTAL_MARKING_LIST = QName.createQName(RM_CUSTOM_URI, "customSupplementalMarkingList"); + + // Custom associations aspect + public static QName ASPECT_CUSTOM_ASSOCIATIONS = QName.createQName(RM_CUSTOM_URI, "customAssocs"); + + // Some Custom references which are present on system startup. + public static QName CUSTOM_REF_VERSIONS = QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_URI, "versions"); + public static QName CUSTOM_REF_SUPERSEDES = QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_URI, "supersedes"); +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementModel.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementModel.java new file mode 100644 index 0000000000..fcfa7c8816 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementModel.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.model; + +import org.alfresco.service.namespace.QName; + +/** + * Helper class containing records management qualified names + * + * @author Roy Wetherall + */ +public interface RecordsManagementModel extends RecordsManagementCustomModel +{ + // Namespace details + public static final String RM_URI = "http://www.alfresco.org/model/recordsmanagement/1.0"; + public static final String RM_PREFIX = "rma"; + + // Model + public static final QName RM_MODEL = QName.createQName(RM_URI, "recordsmanagement"); + + // RM Site + public static final QName TYPE_RM_SITE = QName.createQName(RM_URI, "rmsite"); + + // Caveat config + public static final QName TYPE_CAVEAT_CONFIG = QName.createQName(RM_URI, "caveatConfig"); + + public static final QName ASPECT_CAVEAT_CONFIG_ROOT = QName.createQName(RM_URI, "caveatConfigRoot"); + public static final QName ASSOC_CAVEAT_CONFIG = QName.createQName(RM_URI, "caveatConfigAssoc"); + + // Email config + public static final QName TYPE_EMAIL_CONFIG = QName.createQName(RM_URI, "emailConfig"); + public static final QName ASPECT_EMAIL_CONFIG_ROOT = QName.createQName(RM_URI, "emailConfigRoot"); + public static final QName ASSOC_EMAIL_CONFIG = QName.createQName(RM_URI, "emailConfigAssoc"); + + // Records management container + public static final QName TYPE_RECORDS_MANAGEMENT_CONTAINER = QName.createQName(RM_URI, "recordsManagementContainer"); + + // Record Category + public static final QName TYPE_RECORD_CATEGORY = QName.createQName(RM_URI, "recordCategory"); + + // Records management root container + public static final QName TYPE_FILE_PLAN = QName.createQName(RM_URI, "filePlan"); + + // Disposition instructions aspect + public static final QName ASPECT_SCHEDULED = QName.createQName(RM_URI, "scheduled"); + public static final QName ASSOC_DISPOSITION_SCHEDULE = QName.createQName(RM_URI, "dispositionSchedule"); + + // Disposition definition type + public static final QName TYPE_DISPOSITION_SCHEDULE = QName.createQName(RM_URI, "dispositionSchedule"); + public static final QName PROP_DISPOSITION_AUTHORITY = QName.createQName(RM_URI, "dispositionAuthority"); + public static final QName PROP_DISPOSITION_INSTRUCTIONS = QName.createQName(RM_URI, "dispositionInstructions"); + public static final QName PROP_RECORD_LEVEL_DISPOSITION = QName.createQName(RM_URI, "recordLevelDisposition"); + public static final QName ASSOC_DISPOSITION_ACTION_DEFINITIONS = QName.createQName(RM_URI, "dispositionActionDefinitions"); + + // Disposition action type + public static final QName TYPE_DISPOSITION_ACTION_DEFINITION = QName.createQName(RM_URI, "dispositionActionDefinition"); + public static final QName PROP_DISPOSITION_ACTION_NAME = QName.createQName(RM_URI, "dispositionActionName"); + public static final QName PROP_DISPOSITION_DESCRIPTION = QName.createQName(RM_URI, "dispositionDescription"); + public static final QName PROP_DISPOSITION_PERIOD = QName.createQName(RM_URI, "dispositionPeriod"); + public static final QName PROP_DISPOSITION_PERIOD_PROPERTY = QName.createQName(RM_URI, "dispositionPeriodProperty"); + public static final QName PROP_DISPOSITION_EVENT = QName.createQName(RM_URI, "dispositionEvent"); + public static final QName PROP_DISPOSITION_EVENT_COMBINATION = QName.createQName(RM_URI, "dispositionEventCombination"); + public static final QName PROP_DISPOSITION_LOCATION = QName.createQName(RM_URI, "dispositionLocation"); + + // Records folder + public static final QName TYPE_RECORD_FOLDER = QName.createQName(RM_URI, "recordFolder"); + public static final QName PROP_IS_CLOSED = QName.createQName(RM_URI, "isClosed"); + + // Declared record aspect + public static final QName ASPECT_DECLARED_RECORD = QName.createQName(RM_URI, "declaredRecord"); + public static final QName PROP_DECLARED_AT = QName.createQName(RM_URI, "declaredAt"); + public static final QName PROP_DECLARED_BY = QName.createQName(RM_URI, "declaredBy"); + + // Record aspect + public static final QName ASPECT_RECORD = QName.createQName(RM_URI, "record"); + public static final QName PROP_DATE_FILED = QName.createQName(RM_URI, "dateFiled"); + public static final QName PROP_ORIGINATOR = QName.createQName(RM_URI, "originator"); + public static final QName PROP_ORIGINATING_ORGANIZATION = QName.createQName(RM_URI, "originatingOrganization"); + public static final QName PROP_PUBLICATION_DATE = QName.createQName(RM_URI, "publicationDate"); + public static final QName PROP_MEDIA_TYPE = QName.createQName(RM_URI, "mediaType"); + public static final QName PROP_FORMAT = QName.createQName(RM_URI, "format"); + public static final QName PROP_DATE_RECEIVED = QName.createQName(RM_URI, "dateReceived"); + + // Common record details + public static final QName PROP_LOCATION = QName.createQName(RM_URI, "location"); + + // Fileable aspect + public static final QName ASPECT_FILABLE = QName.createQName(RM_URI, "fileable"); + + // Record component identifier aspect + public static final QName ASPECT_RECORD_COMPONENT_ID = QName.createQName(RM_URI, "recordComponentIdentifier"); + public static final QName PROP_IDENTIFIER = QName.createQName(RM_URI, "identifier"); + public static final QName PROP_DB_UNIQUENESS_ID = QName.createQName(RM_URI, "dbUniquenessId"); + + // Vital record definition aspect + public static final QName ASPECT_VITAL_RECORD_DEFINITION = QName.createQName(RM_URI, "vitalRecordDefinition"); + public static final QName PROP_VITAL_RECORD_INDICATOR = QName.createQName(RM_URI, "vitalRecordIndicator"); + public static final QName PROP_REVIEW_PERIOD = QName.createQName(RM_URI, "reviewPeriod"); + + // Vital record aspect + public static final QName ASPECT_VITAL_RECORD = QName.createQName(RM_URI, "vitalRecord"); + public static final QName PROP_REVIEW_AS_OF = QName.createQName(RM_URI, "reviewAsOf"); + public static final QName PROP_NOTIFICATION_ISSUED = QName.createQName(RM_URI, "notificationIssued"); + + // Cut off aspect + public static final QName ASPECT_CUT_OFF = QName.createQName(RM_URI, "cutOff"); + public static final QName PROP_CUT_OFF_DATE = QName.createQName(RM_URI, "cutOffDate"); + + // Transferred aspect + public static final QName ASPECT_TRANSFERRED = QName.createQName(RM_URI, "transferred"); + + // Ascended aspect + public static final QName ASPECT_ASCENDED = QName.createQName(RM_URI, "ascended"); + + // Disposition schedule aspect + public static final QName ASPECT_DISPOSITION_LIFECYCLE = QName.createQName(RM_URI, "dispositionLifecycle"); + public static final QName ASSOC_NEXT_DISPOSITION_ACTION = QName.createQName(RM_URI, "nextDispositionAction"); + public static final QName ASSOC_DISPOSITION_ACTION_HISTORY = QName.createQName(RM_URI, "dispositionActionHistory"); + + // Disposition action type + public static final QName TYPE_DISPOSITION_ACTION = QName.createQName(RM_URI, "dispositionAction"); + public static final QName PROP_DISPOSITION_ACTION_ID = QName.createQName(RM_URI, "dispositionActionId"); + public static final QName PROP_DISPOSITION_ACTION = QName.createQName(RM_URI, "dispositionAction"); + public static final QName PROP_DISPOSITION_AS_OF = QName.createQName(RM_URI, "dispositionAsOf"); + public static final QName PROP_DISPOSITION_EVENTS_ELIGIBLE = QName.createQName(RM_URI, "dispositionEventsEligible"); + public static final QName PROP_DISPOSITION_ACTION_STARTED_AT = QName.createQName(RM_URI, "dispositionActionStartedAt"); + public static final QName PROP_DISPOSITION_ACTION_STARTED_BY = QName.createQName(RM_URI, "dispositionActionStartedBy"); + public static final QName PROP_DISPOSITION_ACTION_COMPLETED_AT = QName.createQName(RM_URI, "dispositionActionCompletedAt"); + public static final QName PROP_DISPOSITION_ACTION_COMPLETED_BY = QName.createQName(RM_URI, "dispositionActionCompletedBy"); + public static final QName ASSOC_EVENT_EXECUTIONS = QName.createQName(RM_URI, "eventExecutions"); + + // Event execution type + public static final QName TYPE_EVENT_EXECUTION = QName.createQName(RM_URI, "eventExecution"); + public static final QName PROP_EVENT_EXECUTION_NAME = QName.createQName(RM_URI, "eventExecutionName"); + public static final QName PROP_EVENT_EXECUTION_AUTOMATIC = QName.createQName(RM_URI, "eventExecutionAutomatic"); + public static final QName PROP_EVENT_EXECUTION_COMPLETE = QName.createQName(RM_URI, "eventExecutionComplete"); + public static final QName PROP_EVENT_EXECUTION_COMPLETED_BY = QName.createQName(RM_URI, "eventExecutionCompletedBy"); + public static final QName PROP_EVENT_EXECUTION_COMPLETED_AT = QName.createQName(RM_URI, "eventExecutionCompletedAt"); + + // Custom RM data aspect + public static final QName ASPECT_CUSTOM_RM_DATA = QName.createQName(RM_URI, "customRMData"); + + // marker aspect on all RM objercts (except caveat root) + public static final QName ASPECT_FILE_PLAN_COMPONENT = QName.createQName(RM_URI, "filePlanComponent"); + public static final QName PROP_ROOT_NODEREF = QName.createQName(RM_URI, "rootNodeRef"); + + // Non-electronic document + public static final QName TYPE_NON_ELECTRONIC_DOCUMENT = QName.createQName(RM_URI, "nonElectronicDocument"); + + // Records management root aspect + public static final QName ASPECT_RECORDS_MANAGEMENT_ROOT = QName.createQName(RM_URI, "recordsManagementRoot"); + public static final QName ASSOC_HOLDS = QName.createQName(RM_URI, "holds"); + public static final QName ASSOC_TRANSFERS = QName.createQName(RM_URI, "transfers"); + + // Hold type + public static final QName TYPE_HOLD = QName.createQName(RM_URI, "hold"); + public static final QName PROP_HOLD_REASON = QName.createQName(RM_URI, "holdReason"); + public static final QName ASSOC_FROZEN_RECORDS = QName.createQName(RM_URI, "frozenRecords"); + + // Record meta data aspect + public static final QName ASPECT_RECORD_META_DATA = QName.createQName(RM_URI, "recordMetaData"); + + // Frozen aspect + public static final QName ASPECT_FROZEN = QName.createQName(RM_URI, "frozen"); + public static final QName PROP_FROZEN_AT = QName.createQName(RM_URI, "frozenAt"); + public static final QName PROP_FROZEN_BY = QName.createQName(RM_URI, "frozenBy"); + + // Transfer aspect + public static final QName TYPE_TRANSFER = QName.createQName(RM_URI, "transfer"); + public static final QName PROP_TRANSFER_ACCESSION_INDICATOR = QName.createQName(RM_URI, "transferAccessionIndicator"); + public static final QName PROP_TRANSFER_PDF_INDICATOR = QName.createQName(RM_URI, "transferPDFIndicator"); + public static final QName PROP_TRANSFER_LOCATION = QName.createQName(RM_URI, "transferLocation"); + public static final QName ASSOC_TRANSFERRED = QName.createQName(RM_URI, "transferred"); + + // Transferring aspect + public static final QName ASPECT_TRANSFERRING = QName.createQName(RM_URI, "transferring"); + + // Versioned record aspect + public static final QName ASPECT_VERSIONED_RECORD = QName.createQName(RM_URI, "versionedRecord"); + + // Unpublished update aspect + public static final QName ASPECT_UNPUBLISHED_UPDATE = QName.createQName(RM_URI, "unpublishedUpdate"); + public static final QName PROP_UNPUBLISHED_UPDATE = QName.createQName(RM_URI, "unpublishedUpdate"); + public static final QName PROP_UPDATE_TO = QName.createQName(RM_URI, "updateTo"); + public static final QName PROP_UPDATED_PROPERTIES = QName.createQName(RM_URI, "updatedProperties"); + public static final QName PROP_PUBLISH_IN_PROGRESS = QName.createQName(RM_URI, "publishInProgress"); + public static final String UPDATE_TO_DISPOSITION_ACTION_DEFINITION = "dispositionActionDefinition"; + + // Ghosted aspect + public static QName ASPECT_GHOSTED = QName.createQName(RM_URI, "ghosted"); +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementSearchBehaviour.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementSearchBehaviour.java new file mode 100644 index 0000000000..c7f33df3a3 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementSearchBehaviour.java @@ -0,0 +1,699 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.model; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionImpl; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionScheduleImpl; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.module.org_alfresco_module_rm.event.EventCompletionDetails; +import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordDefinition; +import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordService; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.policy.Behaviour.NotificationFrequency; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.Period; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Search Behaviour class. + * + * Manages the collapse of data onto the supporting aspect on the record/record folder + * + * @author Roy Wetherall + */ +public class RecordsManagementSearchBehaviour implements RecordsManagementModel +{ + private static Log logger = LogFactory.getLog(RecordsManagementSearchBehaviour.class); + + /** Search specific elements of the RM model */ + public static final QName ASPECT_RM_SEARCH = QName.createQName(RM_URI, "recordSearch"); + public static final QName PROP_RS_DISPOSITION_ACTION_NAME = QName.createQName(RM_URI, "recordSearchDispositionActionName"); + public static final QName PROP_RS_DISPOSITION_ACTION_AS_OF = QName.createQName(RM_URI, "recordSearchDispositionActionAsOf"); + public static final QName PROP_RS_DISPOSITION_EVENTS_ELIGIBLE = QName.createQName(RM_URI, "recordSearchDispositionEventsEligible"); + public static final QName PROP_RS_DISPOSITION_EVENTS = QName.createQName(RM_URI, "recordSearchDispositionEvents"); + public static final QName PROP_RS_VITAL_RECORD_REVIEW_PERIOD = QName.createQName(RM_URI, "recordSearchVitalRecordReviewPeriod"); + public static final QName PROP_RS_VITAL_RECORD_REVIEW_PERIOD_EXPRESSION = QName.createQName(RM_URI, "recordSearchVitalRecordReviewPeriodExpression"); + public static final QName PROP_RS_DISPOSITION_PERIOD = QName.createQName(RM_URI, "recordSearchDispositionPeriod"); + public static final QName PROP_RS_DISPOSITION_PERIOD_EXPRESSION = QName.createQName(RM_URI, "recordSearchDispositionPeriodExpression"); + public static final QName PROP_RS_HAS_DISPOITION_SCHEDULE = QName.createQName(RM_URI, "recordSearchHasDispositionSchedule"); + public static final QName PROP_RS_DISPOITION_INSTRUCTIONS = QName.createQName(RM_URI, "recordSearchDispositionInstructions"); + public static final QName PROP_RS_DISPOITION_AUTHORITY = QName.createQName(RM_URI, "recordSearchDispositionAuthority"); + public static final QName PROP_RS_HOLD_REASON = QName.createQName(RM_URI, "recordSearchHoldReason"); + + /** Policy component */ + private PolicyComponent policyComponent; + + /** Node service */ + private NodeService nodeService; + + /** Records management service */ + private RecordsManagementService recordsManagementService; + + /** Disposition service */ + private DispositionService dispositionService; + + /** Records management service registry */ + private RecordsManagementServiceRegistry recordsManagementServiceRegistry; + + /** Vital record service */ + private VitalRecordService vitalRecordService; + + /** + * @param nodeService the nodeService to set + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @param dispositionService the disposition service + */ + public void setDispositionService(DispositionService dispositionService) + { + this.dispositionService = dispositionService; + } + + /** + * @param policyComponent the policyComponent to set + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * @param recordsManagementService the records management service + */ + public void setRecordsManagementService(RecordsManagementService recordsManagementService) + { + this.recordsManagementService = recordsManagementService; + } + + /** + * @param recordsManagementServiceRegistry the records management service registry + */ + public void setRecordsManagementServiceRegistry(RecordsManagementServiceRegistry recordsManagementServiceRegistry) + { + this.recordsManagementServiceRegistry = recordsManagementServiceRegistry; + } + + /** + * @param vitalRecordService vital record service + */ + public void setVitalRecordService(VitalRecordService vitalRecordService) + { + this.vitalRecordService = vitalRecordService; + } + + /** Java behaviour */ + private JavaBehaviour onAddSearchAspect = new JavaBehaviour(this, "rmSearchAspectAdd", NotificationFrequency.TRANSACTION_COMMIT); + + /** + * Initialisation method + */ + public void init() + { + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateNode"), + TYPE_DISPOSITION_ACTION, + new JavaBehaviour(this, "dispositionActionCreate", NotificationFrequency.TRANSACTION_COMMIT)); + + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"), + TYPE_DISPOSITION_ACTION, + new JavaBehaviour(this, "dispositionActionPropertiesUpdate", NotificationFrequency.TRANSACTION_COMMIT)); + + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"), + TYPE_DISPOSITION_SCHEDULE, + new JavaBehaviour(this, "dispositionSchedulePropertiesUpdate", NotificationFrequency.TRANSACTION_COMMIT)); + + this.policyComponent.bindAssociationBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateChildAssociation"), + TYPE_DISPOSITION_ACTION, + ASSOC_EVENT_EXECUTIONS, + new JavaBehaviour(this, "eventExecutionUpdate", NotificationFrequency.TRANSACTION_COMMIT)); + + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onDeleteNode"), + TYPE_EVENT_EXECUTION, + new JavaBehaviour(this, "eventExecutionDelete", NotificationFrequency.TRANSACTION_COMMIT)); + + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onAddAspect"), + ASPECT_RM_SEARCH, + onAddSearchAspect); + + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onAddAspect"), + ASPECT_RECORD, + new JavaBehaviour(this, "onAddRecordAspect", NotificationFrequency.TRANSACTION_COMMIT)); + + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateNode"), + TYPE_RECORD_FOLDER, + new JavaBehaviour(this, "recordFolderCreate", NotificationFrequency.TRANSACTION_COMMIT)); + + // Vital Records Review Details Rollup + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onAddAspect"), + ASPECT_VITAL_RECORD_DEFINITION, + new JavaBehaviour(this, "vitalRecordDefintionAddAspect", NotificationFrequency.TRANSACTION_COMMIT)); + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"), + ASPECT_VITAL_RECORD_DEFINITION, + new JavaBehaviour(this, "vitalRecordDefintionUpdateProperties", NotificationFrequency.TRANSACTION_COMMIT)); + + // Hold reason rollup + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onRemoveAspect"), + ASPECT_FROZEN, + new JavaBehaviour(this, "onRemoveFrozenAspect", NotificationFrequency.TRANSACTION_COMMIT)); + + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"), + TYPE_HOLD, + new JavaBehaviour(this, "frozenAspectUpdateProperties", NotificationFrequency.TRANSACTION_COMMIT)); + } + + /** + * Ensures the search aspect for the given node is present, complete and correct. + * + * @param recordOrFolder + */ + public void fixupSearchAspect(NodeRef recordOrFolder) + { + // for now only deal with record folders + if (recordsManagementService.isRecordFolder(recordOrFolder)) + { + // ensure the search aspect is applied + applySearchAspect(recordOrFolder); + + // setup the properties relating to the disposition schedule + setupDispositionScheduleProperties(recordOrFolder); + + // setup the properties relating to the disposition lifecycle + DispositionAction da = dispositionService.getNextDispositionAction(recordOrFolder); + if (da != null) + { + updateDispositionActionProperties(recordOrFolder, da.getNodeRef()); + setupDispositionActionEvents(recordOrFolder, da); + } + + // setup the properties relating to the vital record indicator + setVitalRecordDefintionDetails(recordOrFolder); + } + } + + /** + * Updates the disposition action properties + * + * @param nodeRef + * @param before + * @param after + */ + public void dispositionActionPropertiesUpdate(final NodeRef nodeRef, final Map before, final Map after) + { + if (this.nodeService.exists(nodeRef) == true) + { + AuthenticationUtil.runAs(new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + ChildAssociationRef assoc = nodeService.getPrimaryParent(nodeRef); + if (assoc.getTypeQName().equals(ASSOC_NEXT_DISPOSITION_ACTION) == true) + { + // Get the record (or record folder) + NodeRef record = assoc.getParentRef(); + + // Apply the search aspect + applySearchAspect(record); + + // Update disposition properties + updateDispositionActionProperties(record, nodeRef); + } + + return null; + + }}, AuthenticationUtil.getSystemUserName()); + } + } + + private void applySearchAspect(NodeRef nodeRef) + { + onAddSearchAspect.disable(); + try + { + if (this.nodeService.hasAspect(nodeRef, ASPECT_RM_SEARCH) == false) + { + this.nodeService.addAspect(nodeRef, ASPECT_RM_SEARCH , null); + + if (logger.isDebugEnabled()) + logger.debug("Added search aspect to node: " + nodeRef); + } + } + finally + { + onAddSearchAspect.enable(); + } + } + + public void onAddRecordAspect(NodeRef nodeRef, QName aspectTypeQName) + { + if (nodeService.exists(nodeRef) == true) + { + applySearchAspect(nodeRef); + setupDispositionScheduleProperties(nodeRef); + } + } + + public void recordFolderCreate(ChildAssociationRef childAssocRef) + { + NodeRef nodeRef = childAssocRef.getChildRef(); + if (nodeService.exists(nodeRef) == true) + { + applySearchAspect(nodeRef); + setupDispositionScheduleProperties(nodeRef); + } + } + + private void setupDispositionScheduleProperties(NodeRef recordOrFolder) + { + DispositionSchedule ds = dispositionService.getDispositionSchedule(recordOrFolder); + if (ds == null) + { + nodeService.setProperty(recordOrFolder, PROP_RS_HAS_DISPOITION_SCHEDULE, false); + } + else + { + nodeService.setProperty(recordOrFolder, PROP_RS_HAS_DISPOITION_SCHEDULE, true); + setDispositionScheduleProperties(recordOrFolder, ds); + } + + if (logger.isDebugEnabled()) + { + logger.debug("Set rma:recordSearchHasDispositionSchedule for node " + recordOrFolder + + " to: " + (ds != null)); + } + } + + public void dispositionActionCreate(ChildAssociationRef childAssocRef) + { + NodeRef child = childAssocRef.getChildRef(); + if (nodeService.exists(child) == true && + childAssocRef.getTypeQName().equals(ASSOC_NEXT_DISPOSITION_ACTION) == true) + { + // Get the record (or record folder) + NodeRef record = childAssocRef.getParentRef(); + + // Apply the search aspect + applySearchAspect(record); + + // Update disposition properties + updateDispositionActionProperties(record, childAssocRef.getChildRef()); + + // Clear the events + this.nodeService.setProperty(record, PROP_RS_DISPOSITION_EVENTS, null); + } + } + + /** + * + * @param record + * @param dispositionAction + */ + private void updateDispositionActionProperties(NodeRef record, NodeRef dispositionAction) + { + Map props = nodeService.getProperties(record); + + DispositionAction da = new DispositionActionImpl(recordsManagementServiceRegistry, dispositionAction); + + props.put(PROP_RS_DISPOSITION_ACTION_NAME, da.getName()); + props.put(PROP_RS_DISPOSITION_ACTION_AS_OF, da.getAsOfDate()); + props.put(PROP_RS_DISPOSITION_EVENTS_ELIGIBLE, this.nodeService.getProperty(dispositionAction, PROP_DISPOSITION_EVENTS_ELIGIBLE)); + + DispositionActionDefinition daDefinition = da.getDispositionActionDefinition(); + Period period = daDefinition.getPeriod(); + if (period != null) + { + props.put(PROP_RS_DISPOSITION_PERIOD, period.getPeriodType()); + props.put(PROP_RS_DISPOSITION_PERIOD_EXPRESSION, period.getExpression()); + } + else + { + props.put(PROP_RS_DISPOSITION_PERIOD, null); + props.put(PROP_RS_DISPOSITION_PERIOD_EXPRESSION, null); + } + + nodeService.setProperties(record, props); + + if (logger.isDebugEnabled()) + { + logger.debug("Set rma:recordSearchDispositionActionName for node " + record + " to: " + + props.get(PROP_RS_DISPOSITION_ACTION_NAME)); + logger.debug("Set rma:recordSearchDispositionActionAsOf for node " + record + " to: " + + props.get(PROP_RS_DISPOSITION_ACTION_AS_OF)); + logger.debug("Set rma:recordSearchDispositionEventsEligible for node " + record + " to: " + + props.get(PROP_RS_DISPOSITION_EVENTS_ELIGIBLE)); + logger.debug("Set rma:recordSearchDispositionPeriod for node " + record + " to: " + + props.get(PROP_RS_DISPOSITION_PERIOD)); + logger.debug("Set rma:recordSearchDispositionPeriodExpression for node " + record + " to: " + + props.get(PROP_RS_DISPOSITION_PERIOD_EXPRESSION)); + } + } + + @SuppressWarnings("unchecked") + public void eventExecutionUpdate(ChildAssociationRef childAssocRef, boolean isNewNode) + { + NodeRef dispositionAction = childAssocRef.getParentRef(); + NodeRef eventExecution = childAssocRef.getChildRef(); + + if (this.nodeService.exists(dispositionAction) == true && + this.nodeService.exists(eventExecution) == true) + { + ChildAssociationRef assoc = this.nodeService.getPrimaryParent(dispositionAction); + if (assoc.getTypeQName().equals(ASSOC_NEXT_DISPOSITION_ACTION) == true) + { + // Get the record (or record folder) + NodeRef record = assoc.getParentRef(); + + // Apply the search aspect + applySearchAspect(record); + + Collection events = (List)this.nodeService.getProperty(record, PROP_RS_DISPOSITION_EVENTS); + if (events == null) + { + events = new ArrayList(1); + } + events.add((String)this.nodeService.getProperty(eventExecution, PROP_EVENT_EXECUTION_NAME)); + this.nodeService.setProperty(record, PROP_RS_DISPOSITION_EVENTS, (Serializable)events); + } + } + } + + public void eventExecutionDelete(ChildAssociationRef childAssocRef, boolean isNodeArchived) + { + NodeRef dispositionActionNode = childAssocRef.getParentRef(); + + if (this.nodeService.exists(dispositionActionNode)) + { + ChildAssociationRef assoc = this.nodeService.getPrimaryParent(dispositionActionNode); + if (assoc.getTypeQName().equals(ASSOC_NEXT_DISPOSITION_ACTION) == true) + { + // Get the record (or record folder) + NodeRef record = assoc.getParentRef(); + + // Apply the search aspect + applySearchAspect(record); + + // make sure the list of events match the action definition + setupDispositionActionEvents(record, dispositionService.getNextDispositionAction(record)); + } + } + } + + private void setupDispositionActionEvents(NodeRef nodeRef, DispositionAction da) + { + if (da != null) + { + List eventNames = null; + List eventsList = da.getEventCompletionDetails(); + if (eventsList.size() > 0) + { + eventNames = new ArrayList(eventsList.size()); + for (EventCompletionDetails event : eventsList) + { + eventNames.add(event.getEventName()); + } + } + + // set the property + this.nodeService.setProperty(nodeRef, PROP_RS_DISPOSITION_EVENTS, (Serializable)eventNames); + + if (logger.isDebugEnabled()) + { + logger.debug("Set rma:recordSearchDispositionEvents for node " + nodeRef + " to: " + eventNames); + } + } + } + + public void rmSearchAspectAdd(NodeRef nodeRef, QName aspectTypeQName) + { + if (nodeService.exists(nodeRef) == true) + { + // Initialise the search parameteres as required + setVitalRecordDefintionDetails(nodeRef); + } + } + + public void vitalRecordDefintionAddAspect(NodeRef nodeRef, QName aspectTypeQName) + { + // Only care about record folders + if (recordsManagementService.isRecordFolder(nodeRef) == true) + { + updateVitalRecordDefinitionValues(nodeRef); + } + } + + public void vitalRecordDefintionUpdateProperties(NodeRef nodeRef, Map before, Map after) + { + // Only care about record folders + if (recordsManagementService.isRecordFolder(nodeRef) == true) + { + Set props = new HashSet(1); + props.add(PROP_REVIEW_PERIOD); + Set changed = determineChangedProps(before, after); + changed.retainAll(props); + if (changed.isEmpty() == false) + { + updateVitalRecordDefinitionValues(nodeRef); + } + + } + } + + private void updateVitalRecordDefinitionValues(NodeRef nodeRef) + { + // ensure the folder itself reflects the correct details + applySearchAspect(nodeRef); + setVitalRecordDefintionDetails(nodeRef); + + List records = recordsManagementService.getRecords(nodeRef); + for (NodeRef record : records) + { + // Apply the search aspect + applySearchAspect(record); + + // Set the vital record definition details + setVitalRecordDefintionDetails(record); + } + } + + private void setVitalRecordDefintionDetails(NodeRef nodeRef) + { + VitalRecordDefinition vrd = vitalRecordService.getVitalRecordDefinition(nodeRef); + + if (vrd != null && vrd.isEnabled() == true && vrd.getReviewPeriod() != null) + { + // Set the property values + nodeService.setProperty(nodeRef, PROP_RS_VITAL_RECORD_REVIEW_PERIOD, vrd.getReviewPeriod().getPeriodType()); + nodeService.setProperty(nodeRef, PROP_RS_VITAL_RECORD_REVIEW_PERIOD_EXPRESSION, vrd.getReviewPeriod().getExpression()); + + if (logger.isDebugEnabled()) + { + logger.debug("Set rma:recordSearchVitalRecordReviewPeriod for node " + nodeRef + " to: " + + vrd.getReviewPeriod().getPeriodType()); + logger.debug("Set rma:recordSearchVitalRecordReviewPeriodExpression for node " + nodeRef + " to: " + + vrd.getReviewPeriod().getExpression()); + } + } + else + { + // Clear the vital record properties + nodeService.setProperty(nodeRef, PROP_RS_VITAL_RECORD_REVIEW_PERIOD, null); + nodeService.setProperty(nodeRef, PROP_RS_VITAL_RECORD_REVIEW_PERIOD_EXPRESSION, null); + } + } + + public void onRemoveFrozenAspect(NodeRef nodeRef, QName aspectTypeQName) + { + if (nodeService.exists(nodeRef) == true && + nodeService.hasAspect(nodeRef, ASPECT_RM_SEARCH)) + { + nodeService.setProperty(nodeRef, PROP_RS_HOLD_REASON, null); + } + } + + public void frozenAspectUpdateProperties(final NodeRef nodeRef, final Map before, final Map after) + { + AuthenticationUtil.RunAsWork work = new AuthenticationUtil.RunAsWork() + { + @Override + public Void doWork() throws Exception + { + if (nodeService.exists(nodeRef) == true) + { + // get the changed hold reason + String holdReason = (String)nodeService.getProperty(nodeRef, PROP_HOLD_REASON); + + // get all the frozen items the hold node has and change the hold reason + List holdAssocs = nodeService.getChildAssocs(nodeRef, ASSOC_FROZEN_RECORDS, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef assoc : holdAssocs) + { + NodeRef frozenItem = assoc.getChildRef(); + + // ensure the search aspect is applied and set the hold reason + applySearchAspect(frozenItem); + nodeService.setProperty(frozenItem, PROP_RS_HOLD_REASON, holdReason); + } + } + + return null; + } + }; + + AuthenticationUtil.runAs(work, AuthenticationUtil.getSystemUserName()); + } + + /** + * Updates the disposition schedule properties + * + * @param nodeRef + * @param before + * @param after + */ + public void dispositionSchedulePropertiesUpdate(NodeRef nodeRef, Map before, Map after) + { + if (this.nodeService.exists(nodeRef) == true) + { + // create the schedule object and get the record category for it + DispositionSchedule schedule = new DispositionScheduleImpl(this.recordsManagementServiceRegistry, this.nodeService, nodeRef); + NodeRef recordCategoryNode = this.nodeService.getPrimaryParent(schedule.getNodeRef()).getParentRef(); + + if (schedule.isRecordLevelDisposition()) + { + for (NodeRef recordFolder : this.getRecordFolders(recordCategoryNode)) + { + for (NodeRef record : this.recordsManagementService.getRecords(recordFolder)) + { + applySearchAspect(record); + setDispositionScheduleProperties(record, schedule); + } + } + } + else + { + for (NodeRef recordFolder : this.getRecordFolders(recordCategoryNode)) + { + applySearchAspect(recordFolder); + setDispositionScheduleProperties(recordFolder, schedule); + } + } + } + } + + private void setDispositionScheduleProperties(NodeRef recordOrFolder, DispositionSchedule schedule) + { + if (schedule != null) + { + this.nodeService.setProperty(recordOrFolder, PROP_RS_DISPOITION_AUTHORITY, schedule.getDispositionAuthority()); + this.nodeService.setProperty(recordOrFolder, PROP_RS_DISPOITION_INSTRUCTIONS, schedule.getDispositionInstructions()); + + if (logger.isDebugEnabled()) + { + logger.debug("Set rma:recordSearchDispositionAuthority for node " + recordOrFolder + " to: " + schedule.getDispositionAuthority()); + logger.debug("Set rma:recordSearchDispositionInstructions for node " + recordOrFolder + " to: " + schedule.getDispositionInstructions()); + } + } + } + + /** + * This method compares the oldProps map against the newProps map and returns + * a set of QNames of the properties that have changed. Changed here means one of + *
    + *
  • the property has been removed
  • + *
  • the property has had its value changed
  • + *
  • the property has been added
  • + *
+ */ + private Set determineChangedProps(Map oldProps, Map newProps) + { + Set result = new HashSet(); + for (QName qn : oldProps.keySet()) + { + if (newProps.get(qn) == null || + newProps.get(qn).equals(oldProps.get(qn)) == false) + { + result.add(qn); + } + } + for (QName qn : newProps.keySet()) + { + if (oldProps.get(qn) == null) + { + result.add(qn); + } + } + + return result; + } + + private List getRecordFolders(NodeRef recordCategoryNode) + { + List results = new ArrayList(8); + + List folderAssocs = nodeService.getChildAssocs(recordCategoryNode, + ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef folderAssoc : folderAssocs) + { + NodeRef folder = folderAssoc.getChildRef(); + if (this.recordsManagementService.isRecordFolder(folder)) + { + results.add(folder); + } + } + + return results; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RmSiteType.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RmSiteType.java new file mode 100644 index 0000000000..8224640d20 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RmSiteType.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.model; + +import org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchService; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.policy.Behaviour.NotificationFrequency; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; + +/** + * Behaviour associated with the RM Site type + * + * @author Roy Wetherall + */ +public class RmSiteType implements RecordsManagementModel, + NodeServicePolicies.OnCreateNodePolicy +{ + /** Constant values */ + public static final String COMPONENT_DOCUMENT_LIBRARY = "documentLibrary"; + public static final String DEFAULT_SITE_NAME = "rm"; + + /** Policy component */ + private PolicyComponent policyComponent; + + /** Site service */ + private SiteService siteService; + + /** Node service */ + private NodeService nodeService; + + /** Record Management Search Service */ + private RecordsManagementSearchService recordsManagementSearchService; + + /** + * Set the policy component + * @param policyComponent policy component + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * Set the site service + * @param siteService site service + */ + public void setSiteService(SiteService siteService) + { + this.siteService = siteService; + } + + /** + * Set node service + * @param nodeService node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @param recordsManagementSearchService records management search service + */ + public void setRecordsManagementSearchService(RecordsManagementSearchService recordsManagementSearchService) + { + this.recordsManagementSearchService = recordsManagementSearchService; + } + + /** + * Bean initialisation method + */ + public void init() + { + policyComponent.bindClassBehaviour( + NodeServicePolicies.OnCreateNodePolicy.QNAME, + TYPE_RM_SITE, + new JavaBehaviour(this, "onCreateNode", NotificationFrequency.FIRST_EVENT)); + } + + /** + * @see org.alfresco.repo.node.NodeServicePolicies.OnCreateNodePolicy#onCreateNode(org.alfresco.service.cmr.repository.ChildAssociationRef) + */ + @Override + public void onCreateNode(ChildAssociationRef childAssocRef) + { + final NodeRef rmSite = childAssocRef.getChildRef(); + + // Do not execute behaviour if this has been created in the archive store + if(rmSite.getStoreRef().equals(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE) == true) + { + // This is not the spaces store - probably the archive store + return; + } + + if (nodeService.exists(rmSite) == true) + { + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Object doWork() + { + SiteInfo siteInfo = siteService.getSite(rmSite); + if (siteInfo != null) + { + // Create the file plan component + siteService.createContainer(siteInfo.getShortName(), COMPONENT_DOCUMENT_LIBRARY, TYPE_FILE_PLAN, null); + + // Add the reports + recordsManagementSearchService.addReports(siteInfo.getShortName()); + } + return null; + } + }, AuthenticationUtil.getAdminUserName()); + } + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/ScheduledAspect.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/ScheduledAspect.java new file mode 100644 index 0000000000..22169f0417 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/ScheduledAspect.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.model; + +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.policy.Behaviour.NotificationFrequency; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; + +/** + * Behaviour associated with the scheduled aspect + * + * @author Roy Wetherall + */ +public class ScheduledAspect implements RecordsManagementModel, + NodeServicePolicies.OnAddAspectPolicy +{ + /** Policy component */ + private PolicyComponent policyComponent; + + private DispositionService dispositionService; + + /** Node service */ + private NodeService nodeService; + + /** + * Set the policy component + * @param policyComponent policy component + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + public void setDispositionService(DispositionService dispositionService) + { + this.dispositionService = dispositionService; + } + + /** + * Set node service + * @param nodeService node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Bean initialisation method + */ + public void init() + { + policyComponent.bindClassBehaviour( + NodeServicePolicies.OnAddAspectPolicy.QNAME, + ASPECT_SCHEDULED, + new JavaBehaviour(this, "onAddAspect", NotificationFrequency.TRANSACTION_COMMIT)); + } + + /** + * @see org.alfresco.repo.node.NodeServicePolicies.OnAddAspectPolicy#onAddAspect(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + @Override + public void onAddAspect(NodeRef nodeRef, QName aspectTypeQName) + { + if (nodeService.exists(nodeRef) == true && + dispositionService.getAssociatedDispositionSchedule(nodeRef) == null) + { + dispositionService.createDispositionSchedule(nodeRef, null); + } + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/notification/RecordsManagementNotificationHelper.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/notification/RecordsManagementNotificationHelper.java new file mode 100644 index 0000000000..73e42eb40e --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/notification/RecordsManagementNotificationHelper.java @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.notification; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService; +import org.alfresco.module.org_alfresco_module_rm.security.Role; +import org.alfresco.repo.model.Repository; +import org.alfresco.repo.notification.EMailNotificationProvider; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.service.cmr.notification.NotificationContext; +import org.alfresco.service.cmr.notification.NotificationService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.util.ParameterCheck; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * Helper bean containing methods useful when sending records + * management notifications via the {@link NotificationService} + * + * @author Roy Wetherall + */ +public class RecordsManagementNotificationHelper +{ + /** I18n */ + private static final String MSG_SUBJECT_RECORDS_DUE_FOR_REVIEW = "notification.dueforreview.subject"; + private static final String MSG_SUBJECT_RECORD_SUPERCEDED = "notification.superseded.subject"; + + /** Defaults */ + private static final String DEFAULT_SITE = "rm"; + + /** Services */ + private NotificationService notificationService; + private RecordsManagementService recordsManagementService; + private RecordsManagementSecurityService securityService; + private Repository repositoryHelper; + private SearchService searchService; + private NamespaceService namespaceService; + private SiteService siteService; + + /** Notification role */ + private String notificationRole; + + /** EMail notification templates */ + private NodeRef supersededTemplate = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "record_superseded_template"); + private NodeRef dueForReviewTemplate; + + /** + * @param notificationService notification service + */ + public void setNotificationService(NotificationService notificationService) + { + this.notificationService = notificationService; + } + + /** + * @param recordsManagementService rm service + */ + public void setRecordsManagementService(RecordsManagementService recordsManagementService) + { + this.recordsManagementService = recordsManagementService; + } + + /** + * @param securityService rm security service + */ + public void setSecurityService(RecordsManagementSecurityService securityService) + { + this.securityService = securityService; + } + + /** + * @param notificationRole rm notification role + */ + public void setNotificationRole(String notificationRole) + { + this.notificationRole = notificationRole; + } + + /** + * + * @param repositoryHelper repository helper + */ + public void setRepositoryHelper(Repository repositoryHelper) + { + this.repositoryHelper = repositoryHelper; + } + + /** + * @param searchService search service + */ + public void setSearchService(SearchService searchService) + { + this.searchService = searchService; + } + + /** + * @param namespaceService namespace service + */ + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + /** + * @param siteService site service + */ + public void setSiteService(SiteService siteService) + { + this.siteService = siteService; + } + + /** + * @return superseded email template + */ + public NodeRef getSupersededTemplate() + { + return supersededTemplate; + } + + /** + * @return due for review email template + */ + public NodeRef getDueForReviewTemplate() + { + if (dueForReviewTemplate == null) + { + List nodeRefs = + searchService.selectNodes( + repositoryHelper.getRootHome(), + "app:company_home/app:dictionary/cm:records_management/cm:records_management_email_templates/cm:notify-records-due-for-review-email.ftl", null, + namespaceService, + false); + if (nodeRefs.size() == 1) + { + dueForReviewTemplate = nodeRefs.get(0); + } + } + return dueForReviewTemplate; + } + + /** + * Sends records due for review email notification. + * + * @param records records due for review + */ + public void recordsDueForReviewEmailNotification(final List records) + { + ParameterCheck.mandatory("records", records); + if (records.isEmpty() == false) + { + NodeRef root = getRMRoot(records.get(0)); + + NotificationContext notificationContext = new NotificationContext(); + notificationContext.setSubject(I18NUtil.getMessage(MSG_SUBJECT_RECORDS_DUE_FOR_REVIEW)); + notificationContext.setAsyncNotification(false); + notificationContext.setIgnoreNotificationFailure(true); + + notificationContext.setBodyTemplate(getDueForReviewTemplate()); + Map args = new HashMap(1, 1.0f); + args.put("records", (Serializable)records); + args.put("site", getSiteName(root)); + notificationContext.setTemplateArgs(args); + + String groupName = getGroupName(root); + notificationContext.addTo(groupName); + + notificationService.sendNotification(EMailNotificationProvider.NAME, notificationContext); + } + } + + /** + * Sends record superseded email notification. + * + * @param record superseded record + */ + public void recordSupersededEmailNotification(final NodeRef record) + { + ParameterCheck.mandatory("record", record); + + NodeRef root = getRMRoot(record); + + NotificationContext notificationContext = new NotificationContext(); + notificationContext.setSubject(I18NUtil.getMessage(MSG_SUBJECT_RECORD_SUPERCEDED)); + notificationContext.setAsyncNotification(false); + notificationContext.setIgnoreNotificationFailure(true); + + notificationContext.setBodyTemplate(supersededTemplate); + Map args = new HashMap(1, 1.0f); + args.put("record", record); + args.put("site", getSiteName(root)); + notificationContext.setTemplateArgs(args); + + String groupName = getGroupName(root); + notificationContext.addTo(groupName); + + notificationService.sendNotification(EMailNotificationProvider.NAME, notificationContext); + } + + /** + * Gets the rm root given a context node. + * + * @param context context node reference + * @return {@link NodeRef} rm root node reference + */ + private NodeRef getRMRoot(final NodeRef context) + { + return AuthenticationUtil.runAs(new RunAsWork() + { + @Override + public NodeRef doWork() throws Exception + { + return recordsManagementService.getFilePlan(context); + + } + }, AuthenticationUtil.getSystemUserName()); + + } + + /** + * Gets the group name for the notification role. + * + * @param root rm root node + * @return String notification role's group name + */ + private String getGroupName(final NodeRef root) + { + return AuthenticationUtil.runAs(new RunAsWork() + { + @Override + public String doWork() throws Exception + { + // Find the authority for the given role + Role role = securityService.getRole(root, notificationRole); + return role.getRoleGroupName(); + } + }, AuthenticationUtil.getSystemUserName()); + } + + /** + * Get the site name, default if none/undetermined. + * + * @param root rm root + * @return String site name + */ + private String getSiteName(final NodeRef root) + { + return AuthenticationUtil.runAs(new RunAsWork() + { + @Override + public String doWork() throws Exception + { + String result = DEFAULT_SITE; + + SiteInfo siteInfo = siteService.getSite(root); + if (siteInfo != null) + { + result = siteInfo.getShortName(); + } + + return result; + } + }, AuthenticationUtil.getSystemUserName()); + + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/patch/NotificationTemplatePatch.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/patch/NotificationTemplatePatch.java new file mode 100644 index 0000000000..11c49ea482 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/patch/NotificationTemplatePatch.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.patch; + +import java.io.InputStream; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.notification.RecordsManagementNotificationHelper; +import org.alfresco.repo.module.AbstractModuleComponent; +import org.alfresco.repo.version.VersionModel; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.version.Version; +import org.alfresco.service.cmr.version.VersionService; +import org.alfresco.service.cmr.version.VersionType; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.BeanNameAware; + +/** + * @author Roy Wetherall + */ +public class NotificationTemplatePatch extends AbstractModuleComponent + implements BeanNameAware +{ + /** Last patch update property */ + private static final QName PROP_LAST_PATCH_UPDATE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "lastPatchUpdate"); + + private static final String PATH_DUE_FOR_REVIEW = "alfresco/module/org_alfresco_module_rm/bootstrap/content/notify-records-due-for-review-email.ftl"; + private static final String PATH_SUPERSEDED = "alfresco/module/org_alfresco_module_rm/bootstrap/content/record-superseded-email.ftl"; + + /** Logger */ + private static Log logger = LogFactory.getLog(NotificationTemplatePatch.class); + + /** Records management notification helper */ + private RecordsManagementNotificationHelper notificationHelper; + + /** Node service */ + private NodeService nodeService; + + /** Content service */ + private ContentService contentService; + + /** Version service */ + private VersionService versionService; + + /** Bean name */ + private String name; + + /** + * @param notificationHelper notification helper + */ + public void setNotificationHelper(RecordsManagementNotificationHelper notificationHelper) + { + this.notificationHelper = notificationHelper; + } + + /** + * @param nodeService node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @param contentService content service + */ + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + + /** + * @param versionService version service + */ + public void setVersionService(VersionService versionService) + { + this.versionService = versionService; + } + + /** + * @see org.alfresco.repo.module.AbstractModuleComponent#setBeanName(java.lang.String) + */ + @Override + public void setBeanName(String name) + { + this.name = name; + } + + /** + * @see org.alfresco.repo.module.AbstractModuleComponent#executeInternal() + */ + @Override + protected void executeInternal() throws Throwable + { + if (logger.isDebugEnabled() == true) + { + logger.debug("RM Module NotificationTemplatePatch ..."); + } + + NodeRef supersededTemplate = notificationHelper.getSupersededTemplate(); + updateTemplate(supersededTemplate, PATH_SUPERSEDED); + + NodeRef dueForReviewTemplate = notificationHelper.getDueForReviewTemplate(); + updateTemplate(dueForReviewTemplate, PATH_DUE_FOR_REVIEW); + } + + /** + * Attempt to update the template with the updated version + * + * @param template + * @param updatedTemplate + */ + private void updateTemplate(NodeRef template, String templateUpdate) + { + if (template == null || nodeService.exists(template) == false) + { + if (logger.isDebugEnabled() == true) + { + logger.debug("Skipping template update, because template has not been bootstraped."); + } + } + else + { + // Check to see if this template has already been updated + String lastPatchUpdate = (String)nodeService.getProperty(template, PROP_LAST_PATCH_UPDATE); + if (lastPatchUpdate == null || name.equals(lastPatchUpdate) == false) + { + if (logger.isDebugEnabled() == true) + { + logger.debug("Applying update to template. (template=" + template.toString() + ", templateUpdate=" + templateUpdate + ")"); + } + + // Make sure the template is versionable + if (nodeService.hasAspect(template, ContentModel.ASPECT_VERSIONABLE) == false) + { + nodeService.addAspect(template, ContentModel.ASPECT_VERSIONABLE, null); + + // Create version (before template is updated) + Map versionProperties = new HashMap(2); + versionProperties.put(Version.PROP_DESCRIPTION, "Initial version"); + versionProperties.put(VersionModel.PROP_VERSION_TYPE, VersionType.MINOR); + versionService.createVersion(template, versionProperties); + } + + // Update the content of the template + InputStream is = getClass().getClassLoader().getResourceAsStream(templateUpdate); + ContentWriter writer = contentService.getWriter(template, ContentModel.PROP_CONTENT, true); + writer.putContent(is); + + // Set the last patch update property + nodeService.setProperty(template, PROP_LAST_PATCH_UPDATE, name); + } + else + { + if (logger.isDebugEnabled() == true) + { + logger.debug("Skipping template update, because template has already been patched. (template=" + template.toString() + ")"); + } + } + } + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/patch/RMv2ModelPatch.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/patch/RMv2ModelPatch.java new file mode 100644 index 0000000000..545d846d92 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/patch/RMv2ModelPatch.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.patch; + +import java.util.List; + +import org.alfresco.module.org_alfresco_module_rm.dod5015.DOD5015Model; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.patch.PatchDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.module.AbstractModuleComponent; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.BeanNameAware; + +/** + * RM v2.0 Model Updates Patch + * + * + * @author Roy Wetherall + */ +public class RMv2ModelPatch extends AbstractModuleComponent + implements BeanNameAware, RecordsManagementModel, DOD5015Model +{ + /** Logger */ + private static Log logger = LogFactory.getLog(NotificationTemplatePatch.class); + + private static long BATCH_SIZE = 100000L; + + private PatchDAO patchDAO; + private NodeDAO nodeDAO; + private QNameDAO qnameDAO; + private RetryingTransactionHelper retryingTransactionHelper; + + public void setPatchDAO(PatchDAO patchDAO) + { + this.patchDAO = patchDAO; + } + + public void setNodeDAO(NodeDAO nodeDAO) + { + this.nodeDAO = nodeDAO; + } + + public void setQnameDAO(QNameDAO qnameDAO) + { + this.qnameDAO = qnameDAO; + } + + public void setRetryingTransactionHelper(RetryingTransactionHelper retryingTransactionHelper) + { + this.retryingTransactionHelper = retryingTransactionHelper; + } + + /** + * @see org.alfresco.repo.module.AbstractModuleComponent#executeInternal() + */ + @Override + protected void executeInternal() throws Throwable + { + if (logger.isDebugEnabled() == true) + { + logger.debug("RM Module NotificationTemplatePatch ..."); + } + + updateQName(QName.createQName(DOD_URI, "filePlan"), TYPE_FILE_PLAN, "TYPE"); + updateQName(QName.createQName(DOD_URI, "recordCategory"), TYPE_RECORD_CATEGORY, "TYPE"); + updateQName(QName.createQName(DOD_URI, "ghosted"), ASPECT_GHOSTED, "ASPECT"); + } + + private void updateQName(QName qnameBefore, QName qnameAfter, String reindexClass) + { + Long maxNodeId = patchDAO.getMaxAdmNodeID(); + + Pair before = qnameDAO.getQName(qnameBefore); + + if (before != null) + { + for (Long i = 0L; i < maxNodeId; i+=BATCH_SIZE) + { + Work work = new Work(before.getFirst(), i, reindexClass); + retryingTransactionHelper.doInTransaction(work, false, true); + } + + qnameDAO.updateQName(qnameBefore, qnameAfter); + + if (logger.isDebugEnabled() == true) + { + logger.debug(" ... updated qname " + qnameBefore.toString()); + } + } + else + { + if (logger.isDebugEnabled() == true) + { + logger.debug(" ... no need to update qname " + qnameBefore.toString()); + } + } + } + + private class Work implements RetryingTransactionHelper.RetryingTransactionCallback + { + private long qnameId; + private long lower; + private String reindexClass; + + Work(long qnameId, long lower, String reindexClass) + { + this.qnameId = qnameId; + this.lower = lower; + this.reindexClass = reindexClass; + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback#execute() + */ + @Override + public Integer execute() throws Throwable + { + if ("TYPE".equals(reindexClass)) + { + List nodeIds = patchDAO.getNodesByTypeQNameId(qnameId, lower, lower + BATCH_SIZE); + nodeDAO.touchNodes(nodeDAO.getCurrentTransactionId(true), nodeIds); + return nodeIds.size(); + } + else if ("ASPECT".equals(reindexClass)) + { + List nodeIds = patchDAO.getNodesByAspectQNameId(qnameId, lower, lower + BATCH_SIZE); + nodeDAO.touchNodes(nodeDAO.getCurrentTransactionId(true), nodeIds); + return nodeIds.size(); + } + else + { + // nothing to do + return 0; + } + + } + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/AbstractRmWebScript.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/AbstractRmWebScript.java new file mode 100644 index 0000000000..315d16c0ca --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/AbstractRmWebScript.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.NamespaceService; +import org.json.JSONObject; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * + * @author Neil McErlean + */ +public abstract class AbstractRmWebScript extends DeclarativeWebScript +{ + protected NodeService nodeService; + protected RecordsManagementService rmService; + protected DispositionService dispositionService; + protected NamespaceService namespaceService; + + /** + * Parses the request and providing it's valid returns the NodeRef. + * + * @param req The webscript request + * @return The NodeRef passed in the request + * + * @author Gavin Cornwell + */ + protected NodeRef parseRequestForNodeRef(WebScriptRequest req) + { + // get the parameters that represent the NodeRef, we know they are present + // otherwise this webscript would not have matched + Map templateVars = req.getServiceMatch().getTemplateVars(); + String storeType = templateVars.get("store_type"); + String storeId = templateVars.get("store_id"); + String nodeId = templateVars.get("id"); + + // create the NodeRef and ensure it is valid + StoreRef storeRef = new StoreRef(storeType, storeId); + NodeRef nodeRef = new NodeRef(storeRef, nodeId); + + if (!this.nodeService.exists(nodeRef)) + { + throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, "Unable to find node: " + + nodeRef.toString()); + } + + return nodeRef; + } + + /** + * Sets the RecordsManagementService instance + * + * @param rmService The RecordsManagementService instance + */ + public void setRecordsManagementService(RecordsManagementService rmService) + { + this.rmService = rmService; + } + + /** + * @param dispositionService the disposition serviceS + */ + public void setDispositionService(DispositionService dispositionService) + { + this.dispositionService = dispositionService; + } + + /** + * Sets the NodeService instance + * + * @param nodeService The NodeService instance + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Sets the NamespaceService instance + * + * @param namespaceService The NamespaceService instance + */ + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + /** + * This method checks if the json object contains an entry with the specified name. + * + * @param json the json object. + * @param paramName the name to check for. + * @throws WebScriptException if the specified entry is missing. + */ + protected void checkMandatoryJsonParam(JSONObject json, String paramName) + { + if (json.has(paramName) == false) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Mandatory '" + paramName + "' parameter was not provided in request body"); + } + } + + /** + * This method checks if the json object contains entries with the specified names. + * + * @param json the json object. + * @param paramNames the names to check for. + * @throws WebScriptException if any of the specified entries are missing. + */ + protected void checkMandatoryJsonParams(JSONObject json, List paramNames) + { + for (String name : paramNames) + { + this.checkMandatoryJsonParam(json, name); + } + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/ApplyDodCertModelFixesGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/ApplyDodCertModelFixesGet.java new file mode 100644 index 0000000000..5785be3ae3 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/ApplyDodCertModelFixesGet.java @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminServiceImpl; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementCustomModel; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.dictionary.IndexTokenisationMode; +import org.alfresco.repo.dictionary.M2Aspect; +import org.alfresco.repo.dictionary.M2ClassAssociation; +import org.alfresco.repo.dictionary.M2Model; +import org.alfresco.repo.dictionary.M2Property; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * This webscript applies necessary changes to the RM custom model in the repository. These changes + * are to 'patch' a deployed RM custom model during the DoD certification process. With that in mind + * they are safe to apply to a live database i.e. without side-effect to existing data and safe + * to call multiple times. + *

+ * + * TODO This webscript should be removed after DOD certification as none of these patches are needed + * for a newly-installed DoD amp. + * + * @author neilm + */ +@Deprecated +public class ApplyDodCertModelFixesGet extends DeclarativeWebScript + implements RecordsManagementModel +{ + private static final NodeRef RM_CUSTOM_MODEL_NODE_REF = new NodeRef("workspace://SpacesStore/records_management_custom_model"); + private static final String RMC_CUSTOM_RECORD_SERIES_PROPERTIES = RecordsManagementCustomModel.RM_CUSTOM_PREFIX + ":customRecordSeriesProperties"; + private static final String RMC_CUSTOM_RECORD_CATEGORY_PROPERTIES = RecordsManagementCustomModel.RM_CUSTOM_PREFIX + ":customRecordCategoryProperties"; + private static final String RMC_CUSTOM_RECORD_FOLDER_PROPERTIES = RecordsManagementCustomModel.RM_CUSTOM_PREFIX + ":customRecordFolderProperties"; + private static final String RMC_CUSTOM_RECORD_PROPERTIES = RecordsManagementCustomModel.RM_CUSTOM_PREFIX + ":customRecordProperties"; + + /** Logger */ + private static Log logger = LogFactory.getLog(ApplyDodCertModelFixesGet.class); + + private ContentService contentService; + + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + + @Override + public Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + if (logger.isInfoEnabled()) + { + logger.info("Applying webscript-based patches to RM custom model in the repo."); + } + + M2Model customModel = readCustomContentModel(); + + M2Aspect customAssocsAspect = customModel.getAspect(RecordsManagementAdminServiceImpl.RMC_CUSTOM_ASSOCS); + + if (customAssocsAspect == null) + { + final String msg = "Unknown aspect: "+RecordsManagementAdminServiceImpl.RMC_CUSTOM_ASSOCS; + if (logger.isErrorEnabled()) + { + logger.error(msg); + } + throw new AlfrescoRuntimeException(msg); + } + + + // MOB-1573. All custom references should have many-many multiplicity. + if (logger.isInfoEnabled()) + { + logger.info("MOB-1573. All custom references should have many-many multiplicity."); + } + + for (M2ClassAssociation classAssoc : customAssocsAspect.getAssociations()) + { + classAssoc.setSourceMany(true); + classAssoc.setTargetMany(true); + + } + + + + //MOB-1621. Custom fields should be created as untokenized by default. + if (logger.isInfoEnabled()) + { + logger.info("MOB-1621. Custom fields should be created as untokenized by default."); + } + + List allCustomPropertiesAspects = new ArrayList(4); + allCustomPropertiesAspects.add(RMC_CUSTOM_RECORD_SERIES_PROPERTIES); + allCustomPropertiesAspects.add(RMC_CUSTOM_RECORD_CATEGORY_PROPERTIES); + allCustomPropertiesAspects.add(RMC_CUSTOM_RECORD_FOLDER_PROPERTIES); + allCustomPropertiesAspects.add(RMC_CUSTOM_RECORD_PROPERTIES); + for (String aspectName : allCustomPropertiesAspects) + { + M2Aspect aspectObj = customModel.getAspect(aspectName); + List customProperties = aspectObj.getProperties(); + for (M2Property propertyObj : customProperties) + { + propertyObj.setIndexed(true); + propertyObj.setIndexedAtomically(true); + propertyObj.setStoredInIndex(false); + propertyObj.setIndexTokenisationMode(IndexTokenisationMode.FALSE); + } + } + + + writeCustomContentModel(customModel); + + if (logger.isInfoEnabled()) + { + logger.info("Completed application of webscript-based patches to RM custom model in the repo."); + } + + Map model = new HashMap(1, 1.0f); + model.put("success", true); + + return model; + } + + private M2Model readCustomContentModel() + { + ContentReader reader = this.contentService.getReader(RM_CUSTOM_MODEL_NODE_REF, + ContentModel.TYPE_CONTENT); + + if (reader.exists() == false) {throw new AlfrescoRuntimeException("RM CustomModel has no content.");} + + InputStream contentIn = null; + M2Model deserializedModel = null; + try + { + contentIn = reader.getContentInputStream(); + deserializedModel = M2Model.createModel(contentIn); + } + finally + { + try + { + if (contentIn != null) contentIn.close(); + } + catch (IOException ignored) + { + // Intentionally empty.` + } + } + return deserializedModel; + } + + private void writeCustomContentModel(M2Model deserializedModel) + { + ContentWriter writer = this.contentService.getWriter(RM_CUSTOM_MODEL_NODE_REF, + ContentModel.TYPE_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_XML); + writer.setEncoding("UTF-8"); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + deserializedModel.toXML(baos); + + String updatedModelXml; + try + { + updatedModelXml = baos.toString("UTF-8"); + writer.putContent(updatedModelXml); + // putContent closes all resources. + // so we don't have to. + } catch (UnsupportedEncodingException uex) + { + throw new AlfrescoRuntimeException("Exception when writing custom model xml.", uex); + } + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/ApplyFixMob1573Get.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/ApplyFixMob1573Get.java new file mode 100644 index 0000000000..7aee420818 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/ApplyFixMob1573Get.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminServiceImpl; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.dictionary.M2Aspect; +import org.alfresco.repo.dictionary.M2ClassAssociation; +import org.alfresco.repo.dictionary.M2Model; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * This webscript patches the RM custom model as fix for MOB-1573. It is only necessary for databases + * that had their RM amps initialised before the fix went in. + * There is no side-effect if it is called when it is not needed or if it is called multiple times. + * + * TODO This webscript should be removed after DOD certification. + * + * @author neilm + */ +@Deprecated +public class ApplyFixMob1573Get extends DeclarativeWebScript + implements RecordsManagementModel +{ + private static final NodeRef RM_CUSTOM_MODEL_NODE_REF = new NodeRef("workspace://SpacesStore/records_management_custom_model"); + + private ContentService contentService; + + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + + @Override + public Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + M2Model customModel = readCustomContentModel(); + + // Go through every custom reference defined in the custom model and make sure that it + // has many-to-many multiplicity + String aspectName = RecordsManagementAdminServiceImpl.RMC_CUSTOM_ASSOCS; + M2Aspect customAssocsAspect = customModel.getAspect(aspectName); + + if (customAssocsAspect == null) + { + throw new AlfrescoRuntimeException("Unknown aspect: "+aspectName); + } + + for (M2ClassAssociation classAssoc : customAssocsAspect.getAssociations()) + { + classAssoc.setSourceMany(true); + classAssoc.setTargetMany(true); + } + + writeCustomContentModel(customModel); + + Map model = new HashMap(1, 1.0f); + model.put("success", true); + + return model; + } + + private M2Model readCustomContentModel() + { + ContentReader reader = this.contentService.getReader(RM_CUSTOM_MODEL_NODE_REF, + ContentModel.TYPE_CONTENT); + + if (reader.exists() == false) {throw new AlfrescoRuntimeException("RM CustomModel has no content.");} + + InputStream contentIn = null; + M2Model deserializedModel = null; + try + { + contentIn = reader.getContentInputStream(); + deserializedModel = M2Model.createModel(contentIn); + } + finally + { + try + { + if (contentIn != null) contentIn.close(); + } + catch (IOException ignored) + { + // Intentionally empty.` + } + } + return deserializedModel; + } + + private void writeCustomContentModel(M2Model deserializedModel) + { + ContentWriter writer = this.contentService.getWriter(RM_CUSTOM_MODEL_NODE_REF, + ContentModel.TYPE_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_XML); + writer.setEncoding("UTF-8"); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + deserializedModel.toXML(baos); + + String updatedModelXml; + try + { + updatedModelXml = baos.toString("UTF-8"); + writer.putContent(updatedModelXml); + // putContent closes all resources. + // so we don't have to. + } catch (UnsupportedEncodingException uex) + { + throw new AlfrescoRuntimeException("Exception when writing custom model xml.", uex); + } + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/AuditLogDelete.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/AuditLogDelete.java new file mode 100644 index 0000000000..2330b96fb9 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/AuditLogDelete.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * Implementation for Java backed webscript to clear the + * Records Management audit log. + * + * @author Gavin Cornwell + */ +public class AuditLogDelete extends BaseAuditAdminWebScript +{ + /* + * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.Status, org.alfresco.web.scripts.Cache) + */ + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + this.rmAuditService.clear(); + + // create model object with the audit status model + Map model = new HashMap(1); + model.put("auditstatus", createAuditStatusModel()); + return model; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/AuditLogGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/AuditLogGet.java new file mode 100644 index 0000000000..fae34d324a --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/AuditLogGet.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.io.File; +import java.io.IOException; + +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.springframework.extensions.webscripts.WebScriptResponse; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Implementation for Java backed webscript to return audit + * log of RM events, optionally scoped to an RM node. + * + * @author Gavin Cornwell + */ +public class AuditLogGet extends BaseAuditRetrievalWebScript +{ + /** Logger */ + private static Log logger = LogFactory.getLog(AuditLogGet.class); + + protected final static String PARAM_EXPORT = "export"; + + @Override + public void execute(WebScriptRequest req, WebScriptResponse res) throws IOException + { + File auditTrail = null; + + try + { + // parse the parameters and get a file containing the audit trail + auditTrail = this.rmAuditService.getAuditTrailFile(parseQueryParameters(req), parseReportFormat(req)); + + if (logger.isDebugEnabled()) + logger.debug("Streaming audit trail from file: " + auditTrail.getAbsolutePath()); + + boolean attach = false; + String attachFileName = null; + String export = req.getParameter(PARAM_EXPORT); + if (export != null && Boolean.parseBoolean(export)) + { + attach = true; + attachFileName = auditTrail.getName(); + + if (logger.isDebugEnabled()) + logger.debug("Exporting audit trail using file name: " + attachFileName); + } + + // stream the file back to the client + streamContent(req, res, auditTrail, attach, attachFileName); + } + finally + { + if (auditTrail != null) + { + if (logger.isDebugEnabled()) + { + logger.debug( + "Audit results written to file: \n" + + " File: " + auditTrail + "\n" + + " Parameter: " + parseQueryParameters(req)); + } + else + { + auditTrail.delete(); + } + } + } + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/AuditLogPost.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/AuditLogPost.java new file mode 100644 index 0000000000..f4cd3bd32f --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/AuditLogPost.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.service.cmr.repository.NodeRef; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.springframework.extensions.webscripts.WebScriptResponse; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONObject; +import org.json.JSONTokener; + +/** + * Implementation for Java backed webscript to file an + * audit log as a record. + * + * @author Gavin Cornwell + */ +public class AuditLogPost extends BaseAuditRetrievalWebScript +{ + /** Logger */ + private static Log logger = LogFactory.getLog(AuditLogPost.class); + + protected static final String PARAM_DESTINATION = "destination"; + protected static final String RESPONSE_SUCCESS = "success"; + protected static final String RESPONSE_RECORD = "record"; + protected static final String RESPONSE_RECORD_NAME = "recordName"; + + @Override + public void execute(WebScriptRequest req, WebScriptResponse res) throws IOException + { + try + { + // retrieve requested format + String format = req.getFormat(); + + // construct model for template + Status status = new Status(); + Cache cache = new Cache(getDescription().getRequiredCache()); + Map model = new HashMap(); + model.put("status", status); + model.put("cache", cache); + + // extract the destination parameter, ensure it's present and it is + // a record folder + JSONObject json = new JSONObject(new JSONTokener(req.getContent().getContent())); + if (!json.has(PARAM_DESTINATION)) + { + status.setCode(HttpServletResponse.SC_BAD_REQUEST, + "Mandatory '" + PARAM_DESTINATION + "' parameter has not been supplied"); + Map templateModel = createTemplateParameters(req, res, model); + sendStatus(req, res, status, cache, format, templateModel); + return; + } + + String destinationParam = json.getString(PARAM_DESTINATION); + NodeRef destination = new NodeRef(destinationParam); + + if (!this.nodeService.exists(destination)) + { + status.setCode(HttpServletResponse.SC_NOT_FOUND, + "Node " + destination.toString() + " does not exist"); + Map templateModel = createTemplateParameters(req, res, model); + sendStatus(req, res, status, cache, format, templateModel); + return; + } + + // ensure the node is a filePlan object + if (!RecordsManagementModel.TYPE_RECORD_FOLDER.equals(this.nodeService.getType(destination))) + { + status.setCode(HttpServletResponse.SC_BAD_REQUEST, + "Node " + destination.toString() + " is not a record folder"); + Map templateModel = createTemplateParameters(req, res, model); + sendStatus(req, res, status, cache, format, templateModel); + return; + } + + if (logger.isDebugEnabled()) + logger.debug("Filing audit trail as record in record folder: " + destination); + + // parse the other parameters and get a file containing the audit trail + NodeRef record = this.rmAuditService.fileAuditTrailAsRecord(parseQueryParameters(req), + destination, parseReportFormat(req)); + + if (logger.isDebugEnabled()) + logger.debug("Filed audit trail as new record: " + record); + + // return success flag and record noderef as JSON + JSONObject responseJSON = new JSONObject(); + responseJSON.put(RESPONSE_SUCCESS, (record != null)); + if (record != null) + { + responseJSON.put(RESPONSE_RECORD, record.toString()); + responseJSON.put(RESPONSE_RECORD_NAME, + (String)nodeService.getProperty(record, ContentModel.PROP_NAME)); + } + + // setup response + String jsonString = responseJSON.toString(); + res.setContentType(MimetypeMap.MIMETYPE_JSON); + res.setContentEncoding("UTF-8"); + res.setHeader("Content-Length", Long.toString(jsonString.length())); + + // write the JSON response + res.getWriter().write(jsonString); + } + catch (Throwable e) + { + throw createStatusException(e, req, res); + } + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/AuditLogPut.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/AuditLogPut.java new file mode 100644 index 0000000000..81cdb7e182 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/AuditLogPut.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; + +/** + * Implementation for Java backed webscript to start + * and stop Records Management auditing. + * + * @author Gavin Cornwell + */ +public class AuditLogPut extends BaseAuditAdminWebScript +{ + protected static final String PARAM_ENABLED = "enabled"; + + /* + * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.Status, org.alfresco.web.scripts.Cache) + */ + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + try + { + // determine whether to start or stop auditing + JSONObject json = new JSONObject(new JSONTokener(req.getContent().getContent())); + + // check the enabled property present + if (!json.has(PARAM_ENABLED)) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Mandatory 'enabled' parameter was not provided in request body"); + } + + boolean enabled = json.getBoolean(PARAM_ENABLED); + if (enabled) + { + this.rmAuditService.start(); + } + else + { + this.rmAuditService.stop(); + } + + // create model object with the audit status model + Map model = new HashMap(1); + model.put("auditstatus", createAuditStatusModel()); + return model; + } + catch (IOException iox) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not read content from req.", iox); + } + catch (JSONException je) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not parse JSON from req.", je); + } + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/AuditLogStatusGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/AuditLogStatusGet.java new file mode 100644 index 0000000000..5afb650124 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/AuditLogStatusGet.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditService; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * GET audit log status + * + * @author Roy Wetherall + */ +public class AuditLogStatusGet extends DeclarativeWebScript +{ + /** Records management audit service */ + protected RecordsManagementAuditService rmAuditService; + + /** + * Sets the RecordsManagementAuditService instance + * + * @param auditService The RecordsManagementAuditService instance + */ + public void setRecordsManagementAuditService(RecordsManagementAuditService rmAuditService) + { + this.rmAuditService = rmAuditService; + } + + /** + * @see org.alfresco.repo.web.scripts.content.StreamContent#executeImpl(org.springframework.extensions.webscripts.WebScriptRequest, org.springframework.extensions.webscripts.Status, org.springframework.extensions.webscripts.Cache) + */ + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + Map model = new HashMap(1); + model.put("enabled", Boolean.valueOf(rmAuditService.isEnabled())); + return model; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/BaseAuditAdminWebScript.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/BaseAuditAdminWebScript.java new file mode 100644 index 0000000000..8f5d97a687 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/BaseAuditAdminWebScript.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditService; +import org.springframework.extensions.surf.util.ISO8601DateFormat; +import org.springframework.extensions.webscripts.DeclarativeWebScript; + +/** + * Base class for all audit administration webscripts. + * + * @author Gavin Cornwell + */ +public class BaseAuditAdminWebScript extends DeclarativeWebScript +{ + protected RecordsManagementAuditService rmAuditService; + + /** + * Sets the RecordsManagementAuditService instance + * + * @param auditService The RecordsManagementAuditService instance + */ + public void setRecordsManagementAuditService(RecordsManagementAuditService rmAuditService) + { + this.rmAuditService = rmAuditService; + } + + /** + * Creates a model to represent the current status of the RM audit log. + * + * @return Map of RM audit log status + */ + protected Map createAuditStatusModel() + { + Map auditStatus = new HashMap(3); + + auditStatus.put("started", ISO8601DateFormat.format(rmAuditService.getDateLastStarted())); + auditStatus.put("stopped", ISO8601DateFormat.format(rmAuditService.getDateLastStopped())); + auditStatus.put("enabled", Boolean.valueOf(rmAuditService.isEnabled())); + + return auditStatus; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/BaseAuditRetrievalWebScript.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/BaseAuditRetrievalWebScript.java new file mode 100644 index 0000000000..bcdda05e1b --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/BaseAuditRetrievalWebScript.java @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditQueryParameters; +import org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditService; +import org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditService.ReportFormat; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.web.scripts.content.StreamContent; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.InvalidQNameException; +import org.alfresco.service.namespace.QName; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; + +/** + * Base class for all audit retrieval webscripts. + * + * @author Gavin Cornwell + */ +public class BaseAuditRetrievalWebScript extends StreamContent +{ + /** Logger */ + private static Log logger = LogFactory.getLog(BaseAuditRetrievalWebScript.class); + + protected final static String PARAM_USER = "user"; + protected final static String PARAM_SIZE = "size"; + protected final static String PARAM_EVENT = "event"; + protected final static String PARAM_FROM = "from"; + protected final static String PARAM_TO = "to"; + protected final static String PARAM_PROPERTY = "property"; + protected final static String DATE_PATTERN = "yyyy-MM-dd"; + + protected RecordsManagementAuditService rmAuditService; + + /** + * Sets the RecordsManagementAuditService instance + * + * @param auditService The RecordsManagementAuditService instance + */ + public void setRecordsManagementAuditService(RecordsManagementAuditService rmAuditService) + { + this.rmAuditService = rmAuditService; + } + + /** + * Parses the given request and builds an instance of + * RecordsManagementAuditQueryParameters to retrieve the relevant audit entries + * + * @param req The request + * @return RecordsManagementAuditQueryParameters instance + */ + protected RecordsManagementAuditQueryParameters parseQueryParameters(WebScriptRequest req) + { + // create parameters for audit trail retrieval + RecordsManagementAuditQueryParameters params = new RecordsManagementAuditQueryParameters(); + + // the webscripts can have a couple of different forms of url, work out + // whether a nodeRef has been supplied or whether the whole audit + // log should be displayed + NodeRef nodeRef = null; + Map templateVars = req.getServiceMatch().getTemplateVars(); + String storeType = templateVars.get("store_type"); + if (storeType != null && storeType.length() > 0) + { + // there is a store_type so all other params are likely to be present + String storeId = templateVars.get("store_id"); + String nodeId = templateVars.get("id"); + + // create the nodeRef + nodeRef = new NodeRef(new StoreRef(storeType, storeId), nodeId); + } + + // gather all the common filtering parameters, these could be on the + // query string, in a multipart/form-data request or in a JSON body + String size = null; + String user = null; + String event = null; + String from = null; + String to = null; + String property = null; + + if (MimetypeMap.MIMETYPE_JSON.equals(req.getContentType())) + { + try + { + JSONObject json = new JSONObject(new JSONTokener(req.getContent().getContent())); + if (json.has(PARAM_SIZE)) + { + size = json.getString(PARAM_SIZE); + } + if (json.has(PARAM_USER)) + { + user = json.getString(PARAM_USER); + } + if (json.has(PARAM_EVENT)) + { + event = json.getString(PARAM_EVENT); + } + if (json.has(PARAM_FROM)) + { + from = json.getString(PARAM_FROM); + } + if (json.has(PARAM_TO)) + { + to = json.getString(PARAM_TO); + } + if (json.has(PARAM_PROPERTY)) + { + property = json.getString(PARAM_PROPERTY); + } + } + catch (IOException ioe) + { + // log a warning + if (logger.isWarnEnabled()) + logger.warn("Failed to parse JSON parameters for audit query: " + ioe.getMessage()); + } + catch (JSONException je) + { + // log a warning + if (logger.isWarnEnabled()) + logger.warn("Failed to parse JSON parameters for audit query: " + je.getMessage()); + } + } + else + { + size = req.getParameter(PARAM_SIZE); + user = req.getParameter(PARAM_USER); + event = req.getParameter(PARAM_EVENT); + from = req.getParameter(PARAM_FROM); + to = req.getParameter(PARAM_TO); + property = req.getParameter(PARAM_PROPERTY); + } + + // setup the audit query parameters object + params.setNodeRef(nodeRef); + params.setUser(user); + params.setEvent(event); + + if (size != null && size.length() > 0) + { + try + { + params.setMaxEntries(Integer.parseInt(size)); + } + catch (NumberFormatException nfe) + { + if (logger.isWarnEnabled()) + logger.warn("Ignoring size parameter as '" + size + "' is not a number!"); + } + } + + if (from != null && from.length() > 0) + { + try + { + SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_PATTERN); + params.setDateFrom(dateFormat.parse(from)); + } + catch (ParseException pe) + { + if (logger.isWarnEnabled()) + logger.warn("Ignoring from parameter as '" + from + "' does not conform to the date pattern: " + DATE_PATTERN); + } + } + + if (to != null && to.length() > 0) + { + try + { + SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_PATTERN); + params.setDateTo(dateFormat.parse(to)); + } + catch (ParseException pe) + { + if (logger.isWarnEnabled()) + logger.warn("Ignoring to parameter as '" + to + "' does not conform to the date pattern: " + DATE_PATTERN); + } + } + + if (property != null && property.length() > 0) + { + try + { + params.setProperty(QName.createQName(property)); + } + catch (InvalidQNameException iqe) + { + if (logger.isWarnEnabled()) + logger.warn("Ignoring property parameter as '" + property + "' is an invalid QName"); + } + } + + return params; + } + + /** + * Parses the given request for the format the audit report + * should be returned in + * + * @param req The request + * @return The format for the report + */ + protected ReportFormat parseReportFormat(WebScriptRequest req) + { + String format = req.getFormat(); + + if (format != null) + { + if (format.equalsIgnoreCase("json")) + { + return ReportFormat.JSON; + } + else if (format.equalsIgnoreCase("html")) + { + return ReportFormat.HTML; + } + } + + return ReportFormat.JSON; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/BaseCustomPropertyWebScript.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/BaseCustomPropertyWebScript.java new file mode 100644 index 0000000000..e53ca97bf8 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/BaseCustomPropertyWebScript.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import org.alfresco.module.org_alfresco_module_rm.dod5015.DOD5015Model; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.service.namespace.QName; + +/** + * Base class for all custom property webscripts. + * + * @author Roy Wetherall + */ +public class BaseCustomPropertyWebScript extends AbstractRmWebScript +{ + /** + * Takes the element name and maps it to the QName of the customisable type. The passed element name should be a prefixed + * qname string, but to support previous versions of this API a couple of hard coded checks are made first. + * + * @param elementName + * @return + */ + protected QName mapToTypeQName(String elementName) + { + // Direct matching provided for backward compatibility with RM 1.0 + if ("recordFolder".equalsIgnoreCase(elementName) == true) + { + return RecordsManagementModel.TYPE_RECORD_FOLDER; + } + else if ("record".equalsIgnoreCase(elementName) == true) + { + return RecordsManagementModel.ASPECT_RECORD; + } + else if ("recordCategory".equalsIgnoreCase(elementName) == true) + { + return RecordsManagementModel.TYPE_RECORD_CATEGORY; + } + else if ("recordSeries".equalsIgnoreCase(elementName) == true) + { + return DOD5015Model.TYPE_RECORD_SERIES; + } + else + { + // Try and convert the string to a qname + return QName.createQName(elementName, namespaceService); + } + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/BaseTransferWebScript.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/BaseTransferWebScript.java new file mode 100644 index 0000000000..b37d2e6c9f --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/BaseTransferWebScript.java @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.web.scripts.content.StreamACP; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.springframework.extensions.webscripts.WebScriptResponse; + +/** + * Abstract base class for transfer related web scripts. + * + * @author Gavin Cornwell + */ +public abstract class BaseTransferWebScript extends StreamACP + implements RecordsManagementModel +{ + /** Logger */ + private static Log logger = LogFactory.getLog(BaseTransferWebScript.class); + + /** + * @see org.alfresco.web.scripts.WebScript#execute(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse) + */ + public void execute(WebScriptRequest req, WebScriptResponse res) throws IOException + { + File tempFile = null; + try + { + // retrieve requested format + String format = req.getFormat(); + + // construct model for template + Status status = new Status(); + Cache cache = new Cache(getDescription().getRequiredCache()); + Map model = new HashMap(); + model.put("status", status); + model.put("cache", cache); + + // get the parameters that represent the NodeRef, we know they are present + // otherwise this webscript would not have matched + Map templateVars = req.getServiceMatch().getTemplateVars(); + String storeType = templateVars.get("store_type"); + String storeId = templateVars.get("store_id"); + String nodeId = templateVars.get("id"); + String transferId = templateVars.get("transfer_id"); + + // create and return the file plan NodeRef + NodeRef filePlan = new NodeRef(new StoreRef(storeType, storeId), nodeId); + + if (logger.isDebugEnabled()) + logger.debug("Retrieving transfer '" + transferId + "' from file plan: " + filePlan); + + // ensure the file plan exists + if (!this.nodeService.exists(filePlan)) + { + status.setCode(HttpServletResponse.SC_NOT_FOUND, + "Node " + filePlan.toString() + " does not exist"); + Map templateModel = createTemplateParameters(req, res, model); + sendStatus(req, res, status, cache, format, templateModel); + return; + } + + // ensure the node is a filePlan object + if (!TYPE_FILE_PLAN.equals(this.nodeService.getType(filePlan))) + { + status.setCode(HttpServletResponse.SC_BAD_REQUEST, + "Node " + filePlan.toString() + " is not a file plan"); + Map templateModel = createTemplateParameters(req, res, model); + sendStatus(req, res, status, cache, format, templateModel); + return; + } + + // attempt to find the transfer node + NodeRef transferNode = findTransferNode(filePlan, transferId); + + // send 404 if the transfer is not found + if (transferNode == null) + { + status.setCode(HttpServletResponse.SC_NOT_FOUND, + "Could not locate transfer with id: " + transferId); + Map templateModel = createTemplateParameters(req, res, model); + sendStatus(req, res, status, cache, format, templateModel); + return; + } + + // execute the transfer operation + tempFile = executeTransfer(transferNode, req, res, status, cache); + } + catch (Throwable e) + { + throw createStatusException(e, req, res); + } + finally + { + // try and delete the temporary file (if not in debug mode) + if (tempFile != null) + { + if (logger.isDebugEnabled()) + { + logger.debug("Transfer report saved to temporary file: " + tempFile.getAbsolutePath()); + } + else + { + tempFile.delete(); + } + } + } + } + + /** + * Abstract method subclasses implement to perform the actual logic required. + * + * @param transferNode The transfer node + * @param req The request + * @param res The response + * @param status Status object + * @param cache Cache object + * @return File object representing the file containing the JSON of the report + * @throws IOException + */ + protected abstract File executeTransfer(NodeRef transferNode, + WebScriptRequest req, WebScriptResponse res, + Status status, Cache cache) throws IOException; + + /** + * Finds a transfer object with the given id in the given file plan. + * This method returns null if a transfer with the given id is not found. + * + * @param filePlan The file plan to search + * @param transferId The id of the transfer being requested + * @return The transfer node or null if not found + */ + protected NodeRef findTransferNode(NodeRef filePlan, String transferId) + { + NodeRef transferNode = null; + + // get all the transfer nodes and find the one we need + List assocs = this.nodeService.getChildAssocs(filePlan, + RecordsManagementModel.ASSOC_TRANSFERS, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef child : assocs) + { + if (child.getChildRef().getId().equals(transferId)) + { + transferNode = child.getChildRef(); + break; + } + } + + return transferNode; + } + + /** + * Returns an array of NodeRefs representing the items to be transferred. + * + * @param transferNode The transfer object + * @return Array of NodeRefs + */ + protected NodeRef[] getTransferNodes(NodeRef transferNode) + { + List assocs = this.nodeService.getChildAssocs(transferNode, + RecordsManagementModel.ASSOC_TRANSFERRED, RegexQNamePattern.MATCH_ALL); + NodeRef[] itemsToTransfer = new NodeRef[assocs.size()]; + for (int idx = 0; idx < assocs.size(); idx++) + { + itemsToTransfer[idx] = assocs.get(idx).getChildRef(); + } + + return itemsToTransfer; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/BootstrapTestDataGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/BootstrapTestDataGet.java new file mode 100644 index 0000000000..a0571bf0d5 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/BootstrapTestDataGet.java @@ -0,0 +1,325 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; +import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementSearchBehaviour; +import org.alfresco.module.org_alfresco_module_rm.model.RmSiteType; +import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService; +import org.alfresco.module.org_alfresco_module_rm.security.Role; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.cmr.view.ImporterService; +import org.alfresco.service.cmr.view.Location; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * BootstrapTestData GET WebScript implementation. + */ +public class BootstrapTestDataGet extends DeclarativeWebScript + implements RecordsManagementModel +{ + private static Log logger = LogFactory.getLog(BootstrapTestDataGet.class); + + private static final String ARG_SITE_NAME = "site"; + private static final String ARG_IMPORT = "import"; + + private static final String XML_IMPORT = "alfresco/module/org_alfresco_module_rm/dod5015/DODExampleFilePlan.xml"; + + private static final StoreRef SPACES_STORE = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore"); + + private NodeService nodeService; + private SearchService searchService; + private RecordsManagementService recordsManagementService; + private RecordsManagementActionService recordsManagementActionService; + private ImporterService importerService; + private SiteService siteService; + private PermissionService permissionService; + private RecordsManagementSecurityService recordsManagementSecurityService; + private AuthorityService authorityService; + private RecordsManagementSearchBehaviour recordsManagementSearchBehaviour; + private DispositionService dispositionService; + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setSearchService(SearchService searchService) + { + this.searchService = searchService; + } + + public void setDispositionService(DispositionService dispositionService) + { + this.dispositionService = dispositionService; + } + + public void setRecordsManagementService(RecordsManagementService recordsManagementService) + { + this.recordsManagementService = recordsManagementService; + } + + public void setRecordsManagementActionService(RecordsManagementActionService recordsManagementActionService) + { + this.recordsManagementActionService = recordsManagementActionService; + } + + public void setImporterService(ImporterService importerService) + { + this.importerService = importerService; + } + + public void setSiteService(SiteService siteService) + { + this.siteService = siteService; + } + + public void setPermissionService(PermissionService permissionService) + { + this.permissionService = permissionService; + } + + public void setAuthorityService(AuthorityService authorityService) + { + this.authorityService = authorityService; + } + + public void setRecordsManagementSecurityService(RecordsManagementSecurityService recordsManagementSecurityService) + { + this.recordsManagementSecurityService = recordsManagementSecurityService; + } + + public void setRecordsManagementSearchBehaviour(RecordsManagementSearchBehaviour searchBehaviour) + { + this.recordsManagementSearchBehaviour = searchBehaviour; + } + + @Override + public Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + // resolve import argument + boolean importData = false; + if (req.getParameter(ARG_IMPORT) != null) + { + importData = Boolean.parseBoolean(req.getParameter(ARG_IMPORT)); + } + + // resolve rm site + String siteName = RmSiteType.DEFAULT_SITE_NAME; + if (req.getParameter(ARG_SITE_NAME) != null) + { + siteName = req.getParameter(ARG_SITE_NAME); + } + + if (importData) + { + SiteInfo site = siteService.getSite(siteName); + if (site == null) + { + throw new AlfrescoRuntimeException("Records Management site does not exist: " + siteName); + } + + // resolve documentLibrary (filePlan) container + NodeRef filePlan = siteService.getContainer(siteName, RmSiteType.COMPONENT_DOCUMENT_LIBRARY); + if (filePlan == null) + { + filePlan = siteService.createContainer(siteName, RmSiteType.COMPONENT_DOCUMENT_LIBRARY, TYPE_FILE_PLAN, null); + } + + // import the RM test data ACP into the the provided filePlan node reference + InputStream is = BootstrapTestDataGet.class.getClassLoader().getResourceAsStream(XML_IMPORT); + if (is == null) + { + throw new AlfrescoRuntimeException("The DODExampleFilePlan.xml import file could not be found"); + } + Reader viewReader = new InputStreamReader(is); + Location location = new Location(filePlan); + importerService.importView(viewReader, location, null, null); + } + + // Patch data + BootstrapTestDataGet.patchLoadedData(searchService, nodeService, recordsManagementService, + recordsManagementActionService, permissionService, + authorityService, recordsManagementSecurityService, + recordsManagementSearchBehaviour, + dispositionService); + + Map model = new HashMap(1, 1.0f); + model.put("success", true); + + return model; + } + + /** + * Temp method to patch AMP'ed data + * + * @param searchService + * @param nodeService + * @param recordsManagementService + * @param recordsManagementActionService + */ + public static void patchLoadedData( final SearchService searchService, + final NodeService nodeService, + final RecordsManagementService recordsManagementService, + final RecordsManagementActionService recordsManagementActionService, + final PermissionService permissionService, + final AuthorityService authorityService, + final RecordsManagementSecurityService recordsManagementSecurityService, + final RecordsManagementSearchBehaviour recordManagementSearchBehaviour, + final DispositionService dispositionService) + { + AuthenticationUtil.RunAsWork runAsWork = new AuthenticationUtil.RunAsWork() + { + public Object doWork() throws Exception + { + java.util.List rmRoots = recordsManagementService.getFilePlans(); + logger.info("Bootstraping " + rmRoots.size() + " rm roots ..."); + for (NodeRef rmRoot : rmRoots) + { + if (permissionService.getInheritParentPermissions(rmRoot) == true) + { + logger.info("Updating permissions for rm root: " + rmRoot); + permissionService.setInheritParentPermissions(rmRoot, false); + } + + String allRoleShortName = "AllRoles" + rmRoot.getId(); + String allRoleGroupName = authorityService.getName(AuthorityType.GROUP, allRoleShortName); + + if (authorityService.authorityExists(allRoleGroupName) == false) + { + logger.info("Creating all roles group for root node: " + rmRoot.toString()); + + // Create "all" role group for root node + String allRoles = authorityService.createAuthority(AuthorityType.GROUP, + allRoleShortName, + "All Roles", + null); + + // Put all the role groups in it + Set roles = recordsManagementSecurityService.getRoles(rmRoot); + for (Role role : roles) + { + logger.info(" - adding role group " + role.getRoleGroupName() + " to all roles group"); + authorityService.addAuthority(allRoles, role.getRoleGroupName()); + } + + // Set the permissions + permissionService.setPermission(rmRoot, allRoles, RMPermissionModel.READ_RECORDS, true); + } + } + + // Make sure all the containers do not inherit permissions + ResultSet rs = searchService.query(SPACES_STORE, SearchService.LANGUAGE_LUCENE, "TYPE:\"rma:recordsManagementContainer\""); + try + { + logger.info("Bootstraping " + rs.length() + " record containers ..."); + + for (NodeRef container : rs.getNodeRefs()) + { + String containerName = (String)nodeService.getProperty(container, ContentModel.PROP_NAME); + + // Set permissions + if (permissionService.getInheritParentPermissions(container) == true) + { + logger.info("Updating permissions for record container: " + containerName); + permissionService.setInheritParentPermissions(container, false); + } + } + } + finally + { + rs.close(); + } + + // fix up the test dataset to fire initial events for disposition schedules + rs = searchService.query(SPACES_STORE, SearchService.LANGUAGE_LUCENE, "TYPE:\"rma:recordFolder\""); + try + { + logger.info("Bootstraping " + rs.length() + " record folders ..."); + + for (NodeRef recordFolder : rs.getNodeRefs()) + { + String folderName = (String)nodeService.getProperty(recordFolder, ContentModel.PROP_NAME); + + // Set permissions + if (permissionService.getInheritParentPermissions(recordFolder) == true) + { + logger.info("Updating permissions for record folder: " + folderName); + permissionService.setInheritParentPermissions(recordFolder, false); + } + + if (nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE) == false) + { + // See if the folder has a disposition schedule that needs to be applied + DispositionSchedule ds = dispositionService.getDispositionSchedule(recordFolder); + if (ds != null) + { + // Fire action to "set-up" the folder correctly + logger.info("Setting up bootstraped record folder: " + folderName); + recordsManagementActionService.executeRecordsManagementAction(recordFolder, "setupRecordFolder"); + } + } + + // fixup the search behaviour aspect for the record folder + logger.info("Setting up search aspect for record folder: " + folderName); + recordManagementSearchBehaviour.fixupSearchAspect(recordFolder); + } + } + finally + { + rs.close(); + } + + return null; + } + }; + + AuthenticationUtil.runAs(runAsWork, AuthenticationUtil.getAdminUserName()); + + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomPropertyDefinitionDelete.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomPropertyDefinitionDelete.java new file mode 100644 index 0000000000..8792252392 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomPropertyDefinitionDelete.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.namespace.QName; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONException; + +/** + * Implementation for Java backed webscript to remove RM custom property definitions + * from the custom model. + * + * @author Neil McErlean + */ +public class CustomPropertyDefinitionDelete extends AbstractRmWebScript +{ + private static final String PROP_ID = "propId"; + + private static Log logger = LogFactory.getLog(CustomPropertyDefinitionDelete.class); + + private RecordsManagementAdminService rmAdminService; + + public void setRecordsManagementAdminService(RecordsManagementAdminService rmAdminService) + { + this.rmAdminService = rmAdminService; + } + + /* + * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.Status, org.alfresco.web.scripts.Cache) + */ + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + Map ftlModel = null; + try + { + QName propQName = getPropertyFromReq(req); + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Deleting property definition ").append(propQName); + logger.debug(msg.toString()); + } + ftlModel = removePropertyDefinition(propQName); + } + catch (JSONException je) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not parse JSON from req.", je); + } + + return ftlModel; + } + + private QName getPropertyFromReq(WebScriptRequest req) + { + Map templateVars = req.getServiceMatch().getTemplateVars(); + String propIdString = templateVars.get(PROP_ID); + + QName propQName = this.rmAdminService.getQNameForClientId(propIdString); + Map existingPropDefs = rmAdminService.getCustomPropertyDefinitions(); + + if (existingPropDefs.containsKey(propQName) == false) + { + throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, + "Requested property definition (id:" + propIdString + ") does not exist"); + } + + return propQName; + } + + /** + * Applies custom properties to the specified record node. + */ + protected Map removePropertyDefinition(QName propQName) throws JSONException + { + Map result = new HashMap(); + + rmAdminService.removeCustomPropertyDefinition(propQName); + + result.put("propertyqname", propQName.toPrefixString(namespaceService)); + + return result; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomPropertyDefinitionPost.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomPropertyDefinitionPost.java new file mode 100644 index 0000000000..8682ad83b2 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomPropertyDefinitionPost.java @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.io.IOException; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService; +import org.alfresco.module.org_alfresco_module_rm.dod5015.DOD5015Model; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementCustomModel; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.service.namespace.QName; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * Implementation for Java backed webscript to add RM custom property definitions + * to the custom model. + * + * @author Neil McErlean + */ +public class CustomPropertyDefinitionPost extends BaseCustomPropertyWebScript +{ + protected RecordsManagementAdminService rmAdminService; + + private static final String PARAM_DATATYPE = "dataType"; + private static final String PARAM_TITLE = "title"; + private static final String PARAM_DESCRIPTION = "description"; + private static final String PARAM_DEFAULT_VALUE = "defaultValue"; + private static final String PARAM_MULTI_VALUED = "multiValued"; + private static final String PARAM_MANDATORY = "mandatory"; + private static final String PARAM_PROTECTED = "protected"; + private static final String PARAM_CONSTRAINT_REF = "constraintRef"; + private static final String PARAM_ELEMENT = "element"; + private static final String PARAM_LABEL = "label"; + private static final String PROP_ID = "propId"; + private static final String URL = "url"; + + public void setRecordsManagementAdminService(RecordsManagementAdminService rmAdminService) + { + this.rmAdminService = rmAdminService; + } + + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + JSONObject json = null; + Map ftlModel = null; + try + { + json = new JSONObject(new JSONTokener(req.getContent().getContent())); + + ftlModel = createPropertyDefinition(req, json); + } + catch (IOException iox) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not read content from req.", iox); + } + catch (JSONException je) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not parse JSON from req.", je); + } + + return ftlModel; + } + + /** + * Applies custom properties. + */ + protected Map createPropertyDefinition(WebScriptRequest req, JSONObject json) + throws JSONException + { + Map result = new HashMap(); + Map params = getParamsFromUrlAndJson(req, json); + + QName propertyQName = createNewPropertyDefinition(params); + String localName = propertyQName.getLocalName(); + + result.put(PROP_ID, localName); + + String urlResult = req.getServicePath() + "/" + propertyQName.getLocalName(); + result.put(URL, urlResult); + + return result; + } + + @SuppressWarnings("unchecked") + protected Map getParamsFromUrlAndJson(WebScriptRequest req, JSONObject json) + throws JSONException + { + Map params; + params = new HashMap(); + params.put(PARAM_ELEMENT, req.getParameter(PARAM_ELEMENT)); + + for (Iterator iter = json.keys(); iter.hasNext(); ) + { + String nextKeyString = (String)iter.next(); + String nextValueString = json.getString(nextKeyString); + + params.put(nextKeyString, nextValueString); + } + + return params; + } + + /** + * Create a property definition based on the parameter values provided + * + * @param params parameter values + * @return {@link QName} qname of the newly created custom property + */ + protected QName createNewPropertyDefinition(Map params) + { + // Get the customisable type name + String customisableElement = (String)params.get(PARAM_ELEMENT); + QName customisableType = mapToTypeQName(customisableElement); + + String label = (String)params.get(PARAM_LABEL); + + //According to the wireframes, type here can only be date|text|number + Serializable serializableParam = params.get(PARAM_DATATYPE); + QName type = null; + if (serializableParam != null) + { + if (serializableParam instanceof String) + { + type = QName.createQName((String)serializableParam, namespaceService); + } + else if (serializableParam instanceof QName) + { + type = (QName)serializableParam; + } + else + { + throw new AlfrescoRuntimeException("Unexpected type of dataType param: "+serializableParam+" (expected String or QName)"); + } + } + + // The title is actually generated, so this parameter will be ignored + // by the RMAdminService + String title = (String)params.get(PARAM_TITLE); + String description = (String)params.get(PARAM_DESCRIPTION); + String defaultValue = (String)params.get(PARAM_DEFAULT_VALUE); + + boolean mandatory = false; + serializableParam = params.get(PARAM_MANDATORY); + if (serializableParam != null) + { + mandatory = Boolean.valueOf(serializableParam.toString()); + } + + boolean isProtected = false; + serializableParam = params.get(PARAM_PROTECTED); + if (serializableParam != null) + { + isProtected = Boolean.valueOf(serializableParam.toString()); + } + + boolean multiValued = false; + serializableParam = params.get(PARAM_MULTI_VALUED); + if (serializableParam != null) + { + multiValued = Boolean.valueOf(serializableParam.toString()); + } + + serializableParam = params.get(PARAM_CONSTRAINT_REF); + QName constraintRef = null; + if (serializableParam != null) + { + if (serializableParam instanceof String) + { + constraintRef = QName.createQName((String)serializableParam, namespaceService); + } + else if (serializableParam instanceof QName) + { + constraintRef = (QName)serializableParam; + } + else + { + throw new AlfrescoRuntimeException("Unexpected type of constraintRef param: "+serializableParam+" (expected String or QName)"); + } + } + + // if propId is specified, use it. + QName proposedQName = null; + String propId = (String)params.get(PROP_ID); + if (propId != null) + { + proposedQName = QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_PREFIX, propId, namespaceService); + } + + return rmAdminService.addCustomPropertyDefinition( + proposedQName, + customisableType, + label, + type, + title, + description, + defaultValue, + multiValued, + mandatory, + isProtected, + constraintRef); + } + + + +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomPropertyDefinitionPut.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomPropertyDefinitionPut.java new file mode 100644 index 0000000000..fd4ec16c89 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomPropertyDefinitionPut.java @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.io.IOException; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService; +import org.alfresco.service.namespace.QName; +import org.springframework.extensions.surf.util.ParameterCheck; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; + +/** + * Implementation for Java backed webscript to update RM custom property definitions + * in the custom model. + * + * @author Neil McErlean + */ +public class CustomPropertyDefinitionPut extends BaseCustomPropertyWebScript +{ + private RecordsManagementAdminService rmAdminService; + + private static final String PARAM_LABEL = "label"; + private static final String PARAM_CONSTRAINT_REF = "constraintRef"; + private static final String PROP_ID = "propId"; + private static final String URL = "url"; + + public void setRecordsManagementAdminService(RecordsManagementAdminService rmAdminService) + { + this.rmAdminService = rmAdminService; + } + + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + JSONObject json = null; + Map ftlModel = null; + try + { + json = new JSONObject(new JSONTokener(req.getContent().getContent())); + + ftlModel = handlePropertyDefinitionUpdate(req, json); + } + catch (IOException iox) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not read content from req.", iox); + } + catch (JSONException je) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not parse JSON from req.", je); + } + + return ftlModel; + } + + /** + * Applies custom properties. + */ + protected Map handlePropertyDefinitionUpdate(WebScriptRequest req, JSONObject json) + throws JSONException + { + Map result = new HashMap(); + + Map params = getParamsFromUrlAndJson(req, json); + + QName propertyQName; + propertyQName = updatePropertyDefinition(params); + String localName = propertyQName.getLocalName(); + + result.put(PROP_ID, localName); + + String urlResult = req.getServicePath(); + result.put(URL, urlResult); + + return result; + } + + /** + * If label has a non-null value, it is set on the property def. + * If constraintRef has a non-null value, it is set on this propDef. + * If constraintRef has a null value, all constraints for that propDef are removed. + * + * @param params + * @return + */ + protected QName updatePropertyDefinition(Map params) + { + QName result = null; + + String propId = (String)params.get(PROP_ID); + ParameterCheck.mandatoryString("propId", propId); + + QName propQName = rmAdminService.getQNameForClientId(propId); + if (propQName == null) + { + throw new WebScriptException(Status.STATUS_NOT_FOUND, + "Could not find property definition for: " + propId); + } + + if (params.containsKey(PARAM_LABEL)) + { + String label = (String)params.get(PARAM_LABEL); + result = rmAdminService.setCustomPropertyDefinitionLabel(propQName, label); + } + + if (params.containsKey(PARAM_CONSTRAINT_REF)) + { + String constraintRef = (String)params.get(PARAM_CONSTRAINT_REF); + + if (constraintRef == null) + { + result = rmAdminService.removeCustomPropertyDefinitionConstraints(propQName); + } + else + { + QName constraintRefQName = QName.createQName(constraintRef, namespaceService); + result = rmAdminService.setCustomPropertyDefinitionConstraint(propQName, constraintRefQName); + } + } + return result; + } + + @SuppressWarnings("unchecked") + protected Map getParamsFromUrlAndJson(WebScriptRequest req, JSONObject json) + throws JSONException + { + Map params; + params = new HashMap(); + + Map templateVars = req.getServiceMatch().getTemplateVars(); + String propId = templateVars.get(PROP_ID); + if (propId != null) + { + params.put(PROP_ID, (Serializable)propId); + } + + for (Iterator iter = json.keys(); iter.hasNext(); ) + { + String nextKeyString = (String)iter.next(); + String nextValueString = null; + if (!json.isNull(nextKeyString)) + { + nextValueString = json.getString(nextKeyString); + } + + params.put(nextKeyString, nextValueString); + } + + return params; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomPropertyDefinitionsGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomPropertyDefinitionsGet.java new file mode 100644 index 0000000000..c01a3542be --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomPropertyDefinitionsGet.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * This class provides the implementation for the custompropdefinitions.get webscript. + * + * @author Neil McErlean + */ +public class CustomPropertyDefinitionsGet extends BaseCustomPropertyWebScript +{ + /** Logger */ + private static Log logger = LogFactory.getLog(CustomPropertyDefinitionsGet.class); + + private static final String ELEMENT = "element"; + private static final String PROP_ID = "propId"; + + /** Records management admin service */ + private RecordsManagementAdminService rmAdminService; + + /** + * @param rmAdminService records management admin service + */ + public void setRecordsManagementAdminService(RecordsManagementAdminService rmAdminService) + { + this.rmAdminService = rmAdminService; + } + + /** + * @see org.springframework.extensions.webscripts.DeclarativeWebScript#executeImpl(org.springframework.extensions.webscripts.WebScriptRequest, org.springframework.extensions.webscripts.Status, org.springframework.extensions.webscripts.Cache) + */ + @Override + public Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + Map model = new HashMap(); + + Map templateVars = req.getServiceMatch().getTemplateVars(); + String propId = templateVars.get(PROP_ID); + String elementName = req.getParameter(ELEMENT); + + if (logger.isDebugEnabled() && elementName != null) + { + logger.debug("Getting custom property definitions for elementName " + elementName); + } + else if (logger.isDebugEnabled() && propId != null) + { + logger.debug("Getting custom property definition for propId " + propId); + } + + // If propId has been provided then this is a request for a single custom-property-defn. + // else it is a request for all defined on the specified element. + List propData = new ArrayList(); + if (propId != null) + { + QName propQName = rmAdminService.getQNameForClientId(propId); + PropertyDefinition propDefn = rmAdminService.getCustomPropertyDefinitions().get(propQName); + if (propQName == null || propDefn == null) + { + throw new WebScriptException(Status.STATUS_NOT_FOUND, "Property definition for " + propId + " not found."); + } + propData.add(propDefn); + } + else if (elementName != null) + { + QName customisableType = mapToTypeQName(elementName); + Map currentCustomProps = rmAdminService.getCustomPropertyDefinitions(customisableType); + if (currentCustomProps != null) + { + for (Entry entry : currentCustomProps.entrySet()) + { + propData.add(entry.getValue()); + } + } + } + else + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Either elementName or propId must be specified."); + } + + if (logger.isDebugEnabled()) + { + logger.debug("Retrieved custom property definitions: " + propData); + } + + model.put("customProps", propData); + + return model; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomRefDelete.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomRefDelete.java new file mode 100644 index 0000000000..bc84bb82ce --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomRefDelete.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.QName; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Implementation for Java backed webscript to remove RM custom reference instances + * from a node. + * + * @author Neil McErlean + */ +public class CustomRefDelete extends AbstractRmWebScript +{ + private static Log logger = LogFactory.getLog(CustomRefDelete.class); + + private RecordsManagementAdminService rmAdminService; + + public void setRecordsManagementAdminService(RecordsManagementAdminService rmAdminService) + { + this.rmAdminService = rmAdminService; + } + + /* + * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.Status, org.alfresco.web.scripts.Cache) + */ + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + Map ftlModel = removeCustomReferenceInstance(req); + + return ftlModel; + } + + /** + * Removes custom reference. + */ + protected Map removeCustomReferenceInstance(WebScriptRequest req) + { + NodeRef fromNodeRef = parseRequestForNodeRef(req); + + // Get the toNode from the URL query string. + String storeType = req.getParameter("st"); + String storeId = req.getParameter("si"); + String nodeId = req.getParameter("id"); + + // create the NodeRef and ensure it is valid + StoreRef storeRef = new StoreRef(storeType, storeId); + NodeRef toNodeRef = new NodeRef(storeRef, nodeId); + + if (!this.nodeService.exists(toNodeRef)) + { + throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, "Unable to find to-node: " + + toNodeRef.toString()); + } + + Map result = new HashMap(); + + Map templateVars = req.getServiceMatch().getTemplateVars(); + String clientsRefId = templateVars.get("refId"); + QName qn = rmAdminService.getQNameForClientId(clientsRefId); + if (qn == null) + { + throw new WebScriptException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + "Unable to find reference type: " + clientsRefId); + } + + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Removing reference ").append(qn).append(" from ") + .append(fromNodeRef).append(" to ").append(toNodeRef); + logger.debug(msg.toString()); + } + + rmAdminService.removeCustomReference(fromNodeRef, toNodeRef, qn); + + result.put("success", true); + + return result; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomRefPost.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomRefPost.java new file mode 100644 index 0000000000..053f98ee43 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomRefPost.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; + +/** + * Implementation for Java backed webscript to add RM custom reference instances + * to a node. + * + * @author Neil McErlean + */ +public class CustomRefPost extends AbstractRmWebScript +{ + private static final String TO_NODE = "toNode"; + private static final String REF_ID = "refId"; + + @SuppressWarnings("unused") + private static Log logger = LogFactory.getLog(CustomRefPost.class); + + private RecordsManagementAdminService rmAdminService; + + public void setRecordsManagementAdminService(RecordsManagementAdminService rmAdminService) + { + this.rmAdminService = rmAdminService; + } + + /* + * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.Status, org.alfresco.web.scripts.Cache) + */ + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + JSONObject json = null; + Map ftlModel = null; + try + { + json = new JSONObject(new JSONTokener(req.getContent().getContent())); + + ftlModel = addCustomReferenceInstance(req, json); + } + catch (IOException iox) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not read content from req.", iox); + } + catch (JSONException je) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not parse JSON from req.", je); + } + + return ftlModel; + } + + /** + * Applies custom reference. + */ + protected Map addCustomReferenceInstance(WebScriptRequest req, JSONObject json) throws JSONException + { + NodeRef fromNode = parseRequestForNodeRef(req); + + Map result = new HashMap(); + + String toNodeStg = json.getString(TO_NODE); + NodeRef toNode = new NodeRef(toNodeStg); + + String clientsRefId = json.getString(REF_ID); + QName qn = rmAdminService.getQNameForClientId(clientsRefId); + if (qn == null) + { + throw new WebScriptException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + "Unable to find reference type: " + clientsRefId); + } + + rmAdminService.addCustomReference(fromNode, toNode, qn); + + result.put("success", true); + + return result; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomReferenceDefinitionPost.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomReferenceDefinitionPost.java new file mode 100644 index 0000000000..efe3492ab1 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomReferenceDefinitionPost.java @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.io.IOException; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService; +import org.alfresco.service.namespace.QName; +import org.springframework.extensions.surf.util.ParameterCheck; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; + +/** + * Implementation for Java backed webscript to add RM custom reference definitions + * to the custom model. + * + * @author Neil McErlean + */ +public class CustomReferenceDefinitionPost extends AbstractRmWebScript +{ + private static final String URL = "url"; + private static final String REF_ID = "refId"; + private static final String TARGET = "target"; + private static final String SOURCE = "source"; + private static final String LABEL = "label"; + private static final String REFERENCE_TYPE = "referenceType"; + + private static Log logger = LogFactory.getLog(CustomReferenceDefinitionPost.class); + + private RecordsManagementAdminService rmAdminService; + + public void setRecordsManagementAdminService(RecordsManagementAdminService rmAdminService) + { + this.rmAdminService = rmAdminService; + } + + /* + * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.Status, org.alfresco.web.scripts.Cache) + */ + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + JSONObject json = null; + Map ftlModel = null; + try + { + json = new JSONObject(new JSONTokener(req.getContent().getContent())); + + ftlModel = addCustomReference(req, json); + } + catch (IOException iox) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not read content from req.", iox); + } + catch (JSONException je) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not parse JSON from req.", je); + } + + return ftlModel; + } + + /** + * Applies custom properties. + */ + @SuppressWarnings("unchecked") + protected Map addCustomReference(WebScriptRequest req, JSONObject json) throws JSONException + { + Map result = new HashMap(); + Map params = new HashMap(); + + for (Iterator iter = json.keys(); iter.hasNext(); ) + { + String nextKeyString = (String)iter.next(); + Serializable nextValue = (Serializable)json.get(nextKeyString); + + params.put(nextKeyString, nextValue); + } + String refTypeParam = (String)params.get(REFERENCE_TYPE); + ParameterCheck.mandatory(REFERENCE_TYPE, refTypeParam); + CustomReferenceType refTypeEnum = CustomReferenceType.getEnumFromString(refTypeParam); + + boolean isChildAssoc = refTypeEnum.equals(CustomReferenceType.PARENT_CHILD); + + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Creating custom "); + if (isChildAssoc) + { + msg.append("child "); + } + msg.append("assoc"); + logger.debug(msg.toString()); + } + + QName generatedQName; + if (isChildAssoc) + { + String source = (String)params.get(SOURCE); + String target = (String)params.get(TARGET); + + generatedQName = rmAdminService.addCustomChildAssocDefinition(source, target); + } + else + { + String label = (String)params.get(LABEL); + + generatedQName = rmAdminService.addCustomAssocDefinition(label); + } + + result.put(REFERENCE_TYPE, refTypeParam); + + String qnameLocalName; + if (refTypeParam.equals(CustomReferenceType.BIDIRECTIONAL.toString())) + { + Serializable labelParam = params.get(LABEL); + // label is mandatory for bidirectional refs only + ParameterCheck.mandatory(LABEL, labelParam); + + qnameLocalName = generatedQName.getLocalName(); + result.put(REF_ID, qnameLocalName); + } + else if (refTypeParam.equals(CustomReferenceType.PARENT_CHILD.toString())) + { + Serializable sourceParam = params.get(SOURCE); + Serializable targetParam = params.get(TARGET); + // source,target mandatory for parent/child refs only + ParameterCheck.mandatory(SOURCE, sourceParam); + ParameterCheck.mandatory(TARGET, targetParam); + + qnameLocalName = generatedQName.getLocalName(); + result.put(REF_ID, qnameLocalName); + } + else + { + throw new WebScriptException("Unsupported reference type: " + refTypeParam); + } + result.put(URL, req.getServicePath() + "/" + qnameLocalName); + + result.put("success", Boolean.TRUE); + + return result; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomReferenceDefinitionPut.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomReferenceDefinitionPut.java new file mode 100644 index 0000000000..02186dde62 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomReferenceDefinitionPut.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.io.IOException; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition; +import org.alfresco.service.namespace.QName; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; + +/** + * Implementation for Java backed webscript to update RM custom reference definitions. + * There is currently only support for updating the label (for bidirectional references) or + * the source/target (for parent/child references). + * + * @author Neil McErlean + */ +public class CustomReferenceDefinitionPut extends AbstractRmWebScript +{ + private static final String URL = "url"; + private static final String REF_ID = "refId"; + private static final String TARGET = "target"; + private static final String SOURCE = "source"; + private static final String LABEL = "label"; + + private static Log logger = LogFactory.getLog(CustomReferenceDefinitionPut.class); + + private RecordsManagementAdminService rmAdminService; + + public void setRecordsManagementAdminService(RecordsManagementAdminService rmAdminService) + { + this.rmAdminService = rmAdminService; + } + + /* + * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.Status, org.alfresco.web.scripts.Cache) + */ + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + JSONObject json = null; + Map ftlModel = null; + try + { + json = new JSONObject(new JSONTokener(req.getContent().getContent())); + + ftlModel = updateCustomReference(req, json); + } + catch (IOException iox) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not read content from req.", iox); + } + catch (JSONException je) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not parse JSON from req.", je); + } + + return ftlModel; + } + + /** + * Applies custom properties. + */ + @SuppressWarnings("unchecked") + protected Map updateCustomReference(WebScriptRequest req, JSONObject json) throws JSONException + { + Map result = new HashMap(); + Map params = new HashMap(); + + for (Iterator iter = json.keys(); iter.hasNext(); ) + { + String nextKeyString = (String)iter.next(); + Serializable nextValue = (Serializable)json.get(nextKeyString); + + params.put(nextKeyString, nextValue); + } + + Map templateVars = req.getServiceMatch().getTemplateVars(); + String refId = templateVars.get(REF_ID); + // refId cannot be null as it is defined within the URL + params.put(REF_ID, (Serializable)refId); + + // Ensure that the reference actually exists. + QName refQName = rmAdminService.getQNameForClientId(refId); + if (refQName == null) + { + throw new WebScriptException(Status.STATUS_NOT_FOUND, + "Could not find reference definition for: " + refId); + } + + String newLabel = (String)params.get(LABEL); + String newSource = (String)params.get(SOURCE); + String newTarget = (String)params.get(TARGET); + + // Determine whether it's a bidi or a p/c ref + AssociationDefinition assocDef = rmAdminService.getCustomReferenceDefinitions().get(refQName); + if (assocDef == null) + { + throw new WebScriptException(Status.STATUS_NOT_FOUND, + "Could not find reference definition for: " + refId); + } + + if (assocDef instanceof ChildAssociationDefinition) + { + if (newSource != null || newTarget != null) + { + rmAdminService.updateCustomChildAssocDefinition(refQName, newSource, newTarget); + } + } + else if (newLabel != null) + { + rmAdminService.updateCustomAssocDefinition(refQName, newLabel); + } + + result.put(URL, req.getServicePath()); + result.put("refId", refQName.getLocalName()); + result.put("success", Boolean.TRUE); + + return result; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomReferenceDefinitionsGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomReferenceDefinitionsGet.java new file mode 100644 index 0000000000..208102feba --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomReferenceDefinitionsGet.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * This class provides the implementation for the customrefdefinitions.get webscript. + * + * @author Neil McErlean + */ +public class CustomReferenceDefinitionsGet extends DeclarativeWebScript +{ + private static final String REFERENCE_TYPE = "referenceType"; + private static final String REF_ID = "refId"; + private static final String LABEL = "label"; + private static final String SOURCE = "source"; + private static final String TARGET = "target"; + private static final String CUSTOM_REFS = "customRefs"; + private static Log logger = LogFactory.getLog(CustomReferenceDefinitionsGet.class); + + private RecordsManagementAdminService rmAdminService; + private NamespaceService namespaceService; + + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + public void setRecordsManagementAdminService(RecordsManagementAdminService rmAdminService) + { + this.rmAdminService = rmAdminService; + } + + @Override + public Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + Map model = new HashMap(); + + Map templateVars = req.getServiceMatch().getTemplateVars(); + String refId = templateVars.get(REF_ID); + + + if (logger.isDebugEnabled()) + { + logger.debug("Getting custom reference definitions with refId: " + String.valueOf(refId)); + } + + Map currentCustomRefs = rmAdminService.getCustomReferenceDefinitions(); + + // If refId has been provided then this is a request for a single custom-ref-defn. + // else it is a request for them all. + if (refId != null) + { + QName qn = rmAdminService.getQNameForClientId(refId); + + AssociationDefinition assDef = currentCustomRefs.get(qn); + if (assDef == null) + { + StringBuilder msg = new StringBuilder(); + msg.append("Unable to find reference: ").append(refId); + if (logger.isDebugEnabled()) + { + logger.debug(msg.toString()); + } + throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, + msg.toString()); + } + + currentCustomRefs = new HashMap(1); + currentCustomRefs.put(qn, assDef); + } + + List> listOfReferenceData = new ArrayList>(); + + for (Entry entry : currentCustomRefs.entrySet()) + { + Map data = new HashMap(); + + AssociationDefinition nextValue = entry.getValue(); + + CustomReferenceType referenceType = nextValue instanceof ChildAssociationDefinition ? + CustomReferenceType.PARENT_CHILD : CustomReferenceType.BIDIRECTIONAL; + + data.put(REFERENCE_TYPE, referenceType.toString()); + + // It is the title which stores either the label, or the source and target. + String nextTitle = nextValue.getTitle(); + if (CustomReferenceType.PARENT_CHILD.equals(referenceType)) + { + if (nextTitle != null) + { + String[] sourceAndTarget = rmAdminService.splitSourceTargetId(nextTitle); + data.put(SOURCE, sourceAndTarget[0]); + data.put(TARGET, sourceAndTarget[1]); + data.put(REF_ID, entry.getKey().getLocalName()); + } + } + else if (CustomReferenceType.BIDIRECTIONAL.equals(referenceType)) + { + if (nextTitle != null) + { + data.put(LABEL, nextTitle); + data.put(REF_ID, entry.getKey().getLocalName()); + } + } + else + { + throw new WebScriptException("Unsupported custom reference type: " + referenceType); + } + + listOfReferenceData.add(data); + } + + if (logger.isDebugEnabled()) + { + logger.debug("Retrieved custom reference definitions: " + listOfReferenceData.size()); + } + + model.put(CUSTOM_REFS, listOfReferenceData); + + return model; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomReferenceType.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomReferenceType.java new file mode 100644 index 0000000000..b395aff4ca --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomReferenceType.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +/** + * This enum represents the allowed types of custom references. + * + * @author Neil McErlean + */ +public enum CustomReferenceType +{ + PARENT_CHILD("parentchild"), + BIDIRECTIONAL("bidirectional"); + + private final String printableString; + + private CustomReferenceType(String printableString) + { + this.printableString = printableString; + } + + @Override + public String toString() + { + return this.printableString; + } + + public static CustomReferenceType getEnumFromString(String stg) + { + for (CustomReferenceType type : CustomReferenceType.values()) + { + if (type.printableString.equals(stg)) + { + return type; + } + } + throw new IllegalArgumentException("Unrecognised CustomReferenceType: " + stg); + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomRefsGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomRefsGet.java new file mode 100644 index 0000000000..4a2dfbe997 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomRefsGet.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * This class provides the implementation for the customrefs.get webscript. + * + * @author Neil McErlean + */ +public class CustomRefsGet extends AbstractRmWebScript +{ + private static final String REFERENCE_TYPE = "referenceType"; + private static final String REF_ID = "refId"; + private static final String LABEL = "label"; + private static final String SOURCE = "source"; + private static final String TARGET = "target"; + private static final String PARENT_REF = "parentRef"; + private static final String CHILD_REF = "childRef"; + private static final String SOURCE_REF = "sourceRef"; + private static final String TARGET_REF = "targetRef"; + private static final String CUSTOM_REFS_FROM = "customRefsFrom"; + private static final String CUSTOM_REFS_TO = "customRefsTo"; + private static final String NODE_NAME = "nodeName"; + private static final String NODE_TITLE = "nodeTitle"; + + private static Log logger = LogFactory.getLog(CustomRefsGet.class); + private RecordsManagementAdminService rmAdminService; + + public void setRecordsManagementAdminService(RecordsManagementAdminService rmAdminService) + { + this.rmAdminService = rmAdminService; + } + + @Override + public Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + Map ftlModel = new HashMap(); + + NodeRef node = parseRequestForNodeRef(req); + + if (logger.isDebugEnabled()) + { + logger.debug("Getting custom reference instances for " + node); + } + + // All the references that come 'out' from this node. + List> listOfOutwardReferenceData = new ArrayList>(); + + List assocsFromThisNode = this.rmAdminService.getCustomReferencesFrom(node); + addBidirectionalReferenceData(listOfOutwardReferenceData, assocsFromThisNode); + + List childAssocs = this.rmAdminService.getCustomChildReferences(node); + addParentChildReferenceData(listOfOutwardReferenceData, childAssocs); + + // All the references that come 'in' to this node. + List> listOfInwardReferenceData = new ArrayList>(); + + List toAssocs = this.rmAdminService.getCustomReferencesTo(node); + addBidirectionalReferenceData(listOfInwardReferenceData, toAssocs); + + List parentAssocs = this.rmAdminService.getCustomParentReferences(node); + addParentChildReferenceData(listOfInwardReferenceData, parentAssocs); + + if (logger.isDebugEnabled()) + { + logger.debug("Retrieved custom reference instances: " + assocsFromThisNode); + } + + ftlModel.put(NODE_NAME, nodeService.getProperty(node, ContentModel.PROP_NAME)); + ftlModel.put(NODE_TITLE, nodeService.getProperty(node, ContentModel.PROP_TITLE)); + ftlModel.put(CUSTOM_REFS_FROM, listOfOutwardReferenceData); + ftlModel.put(CUSTOM_REFS_TO, listOfInwardReferenceData); + + return ftlModel; + } + + /** + * This method goes through the associationRefs specified and constructs a Map + * for each assRef. FTL-relevant data are added to that map. The associationRefs must all be + * parent/child references. + * + * @param listOfReferenceData + * @param assocs + */ + private void addParentChildReferenceData(List> listOfReferenceData, + List childAssocs) + { + for (ChildAssociationRef childAssRef : childAssocs) + { + Map data = new HashMap(); + + QName typeQName = childAssRef.getTypeQName(); + + data.put(CHILD_REF, childAssRef.getChildRef().toString()); + data.put(PARENT_REF, childAssRef.getParentRef().toString()); + + AssociationDefinition assDef = rmAdminService.getCustomReferenceDefinitions().get(typeQName); + + if (assDef != null) + { + String compoundTitle = assDef.getTitle(); + + data.put(REF_ID, typeQName.getLocalName()); + + String[] sourceAndTarget = rmAdminService.splitSourceTargetId(compoundTitle); + data.put(SOURCE, sourceAndTarget[0]); + data.put(TARGET, sourceAndTarget[1]); + data.put(REFERENCE_TYPE, CustomReferenceType.PARENT_CHILD.toString()); + + listOfReferenceData.add(data); + } + } + } + + /** + * This method goes through the associationRefs specified and constructs a Map + * for each assRef. FTL-relevant data are added to that map. The associationRefs must all be + * bidirectional references. + * + * @param listOfReferenceData + * @param assocs + */ + private void addBidirectionalReferenceData(List> listOfReferenceData, + List assocs) + { + for (AssociationRef assRef : assocs) + { + Map data = new HashMap(); + + QName typeQName = assRef.getTypeQName(); + AssociationDefinition assDef = rmAdminService.getCustomReferenceDefinitions().get(typeQName); + + if (assDef != null) + { + data.put(LABEL, assDef.getTitle()); + data.put(REF_ID, typeQName.getLocalName()); + data.put(REFERENCE_TYPE, CustomReferenceType.BIDIRECTIONAL.toString()); + data.put(SOURCE_REF, assRef.getSourceRef().toString()); + data.put(TARGET_REF, assRef.getTargetRef().toString()); + + listOfReferenceData.add(data); + } + } + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomisableGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomisableGet.java new file mode 100644 index 0000000000..ea9d52b5a8 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomisableGet.java @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * This class provides the implementation for the customisable.get webscript. + * + * @author Roy Wetherall + */ +public class CustomisableGet extends DeclarativeWebScript +{ + /** Logger */ + @SuppressWarnings("unused") + private static Log logger = LogFactory.getLog(CustomisableGet.class); + + /** Records management admin service */ + private RecordsManagementAdminService rmAdminService; + + /** Dictionary service */ + private DictionaryService dictionaryService; + + /** Namespace service */ + private NamespaceService namespaceService; + + /** + * @param rmAdminService records management admin service + */ + public void setRecordsManagementAdminService(RecordsManagementAdminService rmAdminService) + { + this.rmAdminService = rmAdminService; + } + + /** + * @param namespaceService namespace service + */ + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + /** + * @param dictionaryService dictionary service + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + /** + * @see org.springframework.extensions.webscripts.DeclarativeWebScript#executeImpl(org.springframework.extensions.webscripts.WebScriptRequest, org.springframework.extensions.webscripts.Status, org.springframework.extensions.webscripts.Cache) + */ + @Override + public Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + Map model = new HashMap(); + + Set qnames = rmAdminService.getCustomisable(); + ArrayList items = new ArrayList(qnames.size()); + for (QName qname : qnames) + { + ClassDefinition definition = dictionaryService.getClass(qname); + if (definition != null) + { + String name = qname.toPrefixString(namespaceService); + String title = definition.getTitle(); + if (title == null || title.length() == 0) + { + title = qname.getLocalName(); + } + boolean isAspect = definition.isAspect(); + + items.add(new Item(name, isAspect, title)); + } + } + + // Sort the customisable types and aspects by title + Collections.sort(items, new Comparator() + { + @Override + public int compare(Item o1, Item o2) + { + return o1.title.compareToIgnoreCase(o2.title); + }}); + + model.put("items", items); + return model; + } + + /** + * Model items + */ + public class Item + { + private String name; + private boolean isAspect; + private String title; + + public Item(String name, boolean isAspect, String title) + { + this.name = name; + this.isAspect = isAspect; + this.title = title; + } + + public String getName() + { + return name; + } + + public boolean getIsAspect() + { + return isAspect; + } + + public String getTitle() + { + return title; + } + + @Override + public int hashCode() + { + int var_code = (null == name ? 0 : name.hashCode()); + return 31 + var_code; + } + + @Override + public boolean equals(Object obj) + { + if (obj == null || (obj.getClass() != this.getClass())) + { + return false; + } + else + { + return this.name.equals(((Item)obj).name); + } + } + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionAbstractBase.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionAbstractBase.java new file mode 100644 index 0000000000..e7bca4c641 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionAbstractBase.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEvent; +import org.alfresco.service.cmr.repository.NodeRef; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * Abstract base class for all disposition related java backed webscripts. + * + * @author Gavin Cornwell + */ +public class DispositionAbstractBase extends AbstractRmWebScript +{ + /** + * Parses the request and providing it's valid returns the DispositionSchedule object. + * + * @param req The webscript request + * @return The DispositionSchedule object the request is aimed at + */ + protected DispositionSchedule parseRequestForSchedule(WebScriptRequest req) + { + // get the NodeRef from the request + NodeRef nodeRef = parseRequestForNodeRef(req); + + // Determine whether we are getting the inherited disposition schedule or not + boolean inherited = true; + String inheritedString = req.getParameter("inherited"); + if (inheritedString != null) + { + inherited = Boolean.parseBoolean(inheritedString); + } + + // make sure the node passed in has a disposition schedule attached + DispositionSchedule schedule = null; + if (inherited == true) + { + schedule = this.dispositionService.getDispositionSchedule(nodeRef); + } + else + { + schedule = dispositionService.getAssociatedDispositionSchedule(nodeRef); + } + if (schedule == null) + { + throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, "Node " + + nodeRef.toString() + " does not have a disposition schedule"); + } + + return schedule; + } + + /** + * Parses the request and providing it's valid returns the DispositionActionDefinition object. + * + * @param req The webscript request + * @param schedule The disposition schedule + * @return The DispositionActionDefinition object the request is aimed at + */ + protected DispositionActionDefinition parseRequestForActionDefinition(WebScriptRequest req, + DispositionSchedule schedule) + { + // make sure the requested action definition exists + Map templateVars = req.getServiceMatch().getTemplateVars(); + String actionDefId = templateVars.get("action_def_id"); + DispositionActionDefinition actionDef = schedule.getDispositionActionDefinition(actionDefId); + if (actionDef == null) + { + throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, + "Requested disposition action definition (id:" + actionDefId + ") does not exist"); + } + + return actionDef; + } + + /** + * Helper to create a model to represent the given disposition action definition. + * + * @param actionDef The DispositionActionDefinition instance to generate model for + * @param url The URL for the DispositionActionDefinition + * @return Map representing the model + */ + protected Map createActionDefModel(DispositionActionDefinition actionDef, + String url) + { + Map model = new HashMap(8); + + model.put("id", actionDef.getId()); + model.put("index", actionDef.getIndex()); + model.put("url", url); + model.put("name", actionDef.getName()); + model.put("label", actionDef.getLabel()); + model.put("eligibleOnFirstCompleteEvent", actionDef.eligibleOnFirstCompleteEvent()); + + if (actionDef.getDescription() != null) + { + model.put("description", actionDef.getDescription()); + } + + if (actionDef.getPeriod() != null) + { + model.put("period", actionDef.getPeriod().toString()); + } + + if (actionDef.getPeriodProperty() != null) + { + model.put("periodProperty", actionDef.getPeriodProperty().toPrefixString(this.namespaceService)); + } + + if (actionDef.getLocation() != null) + { + model.put("location", actionDef.getLocation()); + } + + List events = actionDef.getEvents(); + if (events != null && events.size() > 0) + { + List eventNames = new ArrayList(events.size()); + for (RecordsManagementEvent event : events) + { + eventNames.add(event.getName()); + } + model.put("events", eventNames); + } + + return model; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionActionDefinitionDelete.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionActionDefinitionDelete.java new file mode 100644 index 0000000000..453219b878 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionActionDefinitionDelete.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * Implementation for Java backed webscript to delete a dispostion action definition. + * + * @author Gavin Cornwell + */ +public class DispositionActionDefinitionDelete extends DispositionAbstractBase +{ + /* + * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.Status, org.alfresco.web.scripts.Cache) + */ + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + // parse the request to retrieve the schedule object + DispositionSchedule schedule = parseRequestForSchedule(req); + + // parse the request to retrieve the action definition object + DispositionActionDefinition actionDef = parseRequestForActionDefinition(req, schedule); + + // remove the action definition from the schedule + this.dispositionService.removeDispositionActionDefinition(schedule, actionDef); + + // return an empty model + return new HashMap(); + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionActionDefinitionPost.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionActionDefinitionPost.java new file mode 100644 index 0000000000..4ec827165c --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionActionDefinitionPost.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.service.namespace.QName; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; + +/** + * Implementation for Java backed webscript to create a new dispositon action definition. + * + * @author Gavin Cornwell + */ +public class DispositionActionDefinitionPost extends DispositionAbstractBase +{ + /* + * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.Status, org.alfresco.web.scripts.Cache) + */ + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + // parse the request to retrieve the schedule object + DispositionSchedule schedule = parseRequestForSchedule(req); + + // retrieve the rest of the post body and create the action + // definition + JSONObject json = null; + DispositionActionDefinition actionDef = null; + try + { + json = new JSONObject(new JSONTokener(req.getContent().getContent())); + actionDef = createActionDefinition(json, schedule); + } + catch (IOException iox) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not read content from req.", iox); + } + catch (JSONException je) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not parse JSON from req.", je); + } + + // create model object with just the action data + Map model = new HashMap(1); + model.put("action", createActionDefModel(actionDef, req.getURL() + "/" + actionDef.getId())); + return model; + } + + /** + * Creates a dispositionActionDefinition node in the repo. + * + * @param json The JSON to use to create the action definition + * @param schedule The DispositionSchedule the action is for + * @return The DispositionActionDefinition representing the new action definition + */ + protected DispositionActionDefinition createActionDefinition(JSONObject json, + DispositionSchedule schedule) throws JSONException + { + // extract the data from the JSON request + if (json.has("name") == false) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Mandatory 'name' parameter was not provided in request body"); + } + + // create the properties for the action definition + Map props = new HashMap(8); + String name = json.getString("name"); + props.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, name); + + if (json.has("description")) + { + props.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, json.getString("description")); + } + + if (json.has("period")) + { + props.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, json.getString("period")); + } + + if (json.has("periodProperty")) + { + QName periodProperty = QName.createQName(json.getString("periodProperty"), this.namespaceService); + props.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD_PROPERTY, periodProperty); + } + + if (json.has("eligibleOnFirstCompleteEvent")) + { + props.put(RecordsManagementModel.PROP_DISPOSITION_EVENT_COMBINATION, + json.getBoolean("eligibleOnFirstCompleteEvent") ? "or" : "and"); + } + + if (json.has("location")) + { + props.put(RecordsManagementModel.PROP_DISPOSITION_LOCATION, + json.getString("location")); + } + + if (json.has("events")) + { + JSONArray events = json.getJSONArray("events"); + List eventsList = new ArrayList(events.length()); + for (int x = 0; x < events.length(); x++) + { + eventsList.add(events.getString(x)); + } + props.put(RecordsManagementModel.PROP_DISPOSITION_EVENT, (Serializable)eventsList); + } + + // add the action definition to the schedule + return this.dispositionService.addDispositionActionDefinition(schedule, props); + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionActionDefinitionPut.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionActionDefinitionPut.java new file mode 100644 index 0000000000..1a1b112f38 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionActionDefinitionPut.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.service.namespace.QName; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; + +/** + * Implementation for Java backed webscript to update an existing dispositon action definition. + * + * @author Gavin Cornwell + */ +public class DispositionActionDefinitionPut extends DispositionAbstractBase +{ + /* + * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.Status, org.alfresco.web.scripts.Cache) + */ + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + // parse the request to retrieve the schedule object + DispositionSchedule schedule = parseRequestForSchedule(req); + + // parse the request to retrieve the action definition object + DispositionActionDefinition actionDef = parseRequestForActionDefinition(req, schedule); + + // retrieve the rest of the post body and update the action definition + JSONObject json = null; + try + { + json = new JSONObject(new JSONTokener(req.getContent().getContent())); + actionDef = updateActionDefinition(actionDef, json); + } + catch (IOException iox) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not read content from req.", iox); + } + catch (JSONException je) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not parse JSON from req.", je); + } + + // create model object with just the action data + Map model = new HashMap(1); + model.put("action", createActionDefModel(actionDef, req.getURL())); + return model; + } + + /** + * Updates a dispositionActionDefinition node in the repo. + * + * @param actionDef The action definition to update + * @param json The JSON to use to create the action definition + * @param schedule The DispositionSchedule the action definition belongs to + * @return The updated DispositionActionDefinition + */ + protected DispositionActionDefinition updateActionDefinition(DispositionActionDefinition actionDef, + JSONObject json) throws JSONException + { + // create the properties for the action definition + Map props = new HashMap(8); + + if (json.has("name")) + { + props.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, json.getString("name")); + } + + if (json.has("description")) + { + props.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, json.getString("description")); + } + + if (json.has("period")) + { + props.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, json.getString("period")); + } + + if (json.has("periodProperty")) + { + QName periodProperty = QName.createQName(json.getString("periodProperty"), this.namespaceService); + props.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD_PROPERTY, periodProperty); + } + + if (json.has("eligibleOnFirstCompleteEvent")) + { + props.put(RecordsManagementModel.PROP_DISPOSITION_EVENT_COMBINATION, + json.getBoolean("eligibleOnFirstCompleteEvent") ? "or" : "and"); + } + + if (json.has("location")) + { + props.put(RecordsManagementModel.PROP_DISPOSITION_LOCATION, + json.getString("location")); + } + + if (json.has("events")) + { + JSONArray events = json.getJSONArray("events"); + List eventsList = new ArrayList(events.length()); + for (int x = 0; x < events.length(); x++) + { + eventsList.add(events.getString(x)); + } + props.put(RecordsManagementModel.PROP_DISPOSITION_EVENT, (Serializable)eventsList); + } + + // update the action definition + return this.dispositionService.updateDispositionActionDefinition(actionDef, props); + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionLifecycleGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionLifecycleGet.java new file mode 100644 index 0000000000..f6dfe3b76e --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionLifecycleGet.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction; +import org.alfresco.module.org_alfresco_module_rm.event.EventCompletionDetails; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.PersonService; +import org.springframework.extensions.surf.util.ISO8601DateFormat; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * Implementation for Java backed webscript to return full details + * about a disposition lifecycle (next disposition action). + * + * @author Gavin Cornwell + */ +public class DispositionLifecycleGet extends DispositionAbstractBase +{ + PersonService personService; + + /** + * Sets the PersonService instance + * + * @param personService The PersonService instance + */ + public void setPersonService(PersonService personService) + { + this.personService = personService; + } + + /* + * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.Status, org.alfresco.web.scripts.Cache) + */ + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + // parse the request to retrieve the next action + NodeRef nodeRef = parseRequestForNodeRef(req); + + // make sure the node passed in has a next action attached + DispositionAction nextAction = this.dispositionService.getNextDispositionAction(nodeRef); + if (nextAction == null) + { + status.setCode(HttpServletResponse.SC_NOT_FOUND, + "Node " + nodeRef.toString() + " does not have a disposition lifecycle"); + return null; + } + else + { + // add all the next action data to Map + Map nextActionModel = new HashMap(8); + String serviceUrl = req.getServiceContextPath() + req.getPathInfo(); + nextActionModel.put("url", serviceUrl); + nextActionModel.put("name", nextAction.getName()); + nextActionModel.put("label", nextAction.getLabel()); + nextActionModel.put("eventsEligible", this.dispositionService.isNextDispositionActionEligible(nodeRef)); + + if (nextAction.getAsOfDate() != null) + { + nextActionModel.put("asOf", ISO8601DateFormat.format(nextAction.getAsOfDate())); + } + + if (nextAction.getStartedAt() != null) + { + nextActionModel.put("startedAt", ISO8601DateFormat.format(nextAction.getStartedAt())); + } + + String startedBy = nextAction.getStartedBy(); + if (startedBy != null) + { + nextActionModel.put("startedBy", startedBy); + addUsersRealName(nextActionModel, startedBy, "startedBy"); + } + + if (nextAction.getCompletedAt() != null) + { + nextActionModel.put("completedAt", ISO8601DateFormat.format(nextAction.getCompletedAt())); + } + + String completedBy = nextAction.getCompletedBy(); + if (completedBy != null) + { + nextActionModel.put("completedBy", completedBy); + addUsersRealName(nextActionModel, completedBy, "completedBy"); + } + + List> events = new ArrayList>(); + for (EventCompletionDetails event : nextAction.getEventCompletionDetails()) + { + events.add(createEventModel(event)); + } + nextActionModel.put("events", events); + + // create model object with just the schedule data + Map model = new HashMap(1); + model.put("nextaction", nextActionModel); + return model; + } + } + + /** + * Helper to create a model to represent the given event execution. + * + * @param event The event to create a model for + * @return Map representing the model + */ + protected Map createEventModel(EventCompletionDetails event) + { + Map model = new HashMap(8); + + model.put("name", event.getEventName()); + model.put("label", event.getEventLabel()); + model.put("automatic", event.isEventExecutionAutomatic()); + model.put("complete", event.isEventComplete()); + + String completedBy = event.getEventCompletedBy(); + if (completedBy != null) + { + model.put("completedBy", completedBy); + addUsersRealName(model, completedBy, "completedBy"); + } + + if (event.getEventCompletedAt() != null) + { + model.put("completedAt", ISO8601DateFormat.format(event.getEventCompletedAt())); + } + + return model; + } + + /** + * Adds the given username's first and last name to the given model. + * + * @param model The model to add the first and last name to + * @param userName The username of the user to lookup + * @param propertyPrefix The prefix of the property name to use when adding to the model + */ + protected void addUsersRealName(Map model, String userName, String propertyPrefix) + { + NodeRef user = this.personService.getPerson(userName); + if (user != null) + { + String firstName = (String)this.nodeService.getProperty(user, ContentModel.PROP_FIRSTNAME); + if (firstName != null) + { + model.put(propertyPrefix + "FirstName", firstName); + } + + String lastName = (String)this.nodeService.getProperty(user, ContentModel.PROP_LASTNAME); + if (lastName != null) + { + model.put(propertyPrefix + "LastName", lastName); + } + } + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionScheduleGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionScheduleGet.java new file mode 100644 index 0000000000..c9775259ef --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionScheduleGet.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * Implementation for Java backed webscript to return full details + * about a disposition schedule. + * + * @author Gavin Cornwell + */ +public class DispositionScheduleGet extends DispositionAbstractBase +{ + /* + * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.Status, org.alfresco.web.scripts.Cache) + */ + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + // parse the request to retrieve the schedule object + DispositionSchedule schedule = parseRequestForSchedule(req); + + // add all the schedule data to Map + Map scheduleModel = new HashMap(8); + + // build url + String serviceUrl = req.getServiceContextPath() + req.getPathInfo(); + scheduleModel.put("url", serviceUrl); + String actionsUrl = serviceUrl + "/dispositionactiondefinitions"; + scheduleModel.put("actionsUrl", actionsUrl); + scheduleModel.put("nodeRef", schedule.getNodeRef().toString()); + scheduleModel.put("recordLevelDisposition", schedule.isRecordLevelDisposition()); + scheduleModel.put("canStepsBeRemoved", + !this.dispositionService.hasDisposableItems(schedule)); + + if (schedule.getDispositionAuthority() != null) + { + scheduleModel.put("authority", schedule.getDispositionAuthority()); + } + + if (schedule.getDispositionInstructions() != null) + { + scheduleModel.put("instructions", schedule.getDispositionInstructions()); + } + + boolean unpublishedUpdates = false; + boolean publishInProgress = false; + + List> actions = new ArrayList>(); + for (DispositionActionDefinition actionDef : schedule.getDispositionActionDefinitions()) + { + NodeRef actionDefNodeRef = actionDef.getNodeRef(); + if (nodeService.hasAspect(actionDefNodeRef, RecordsManagementModel.ASPECT_UNPUBLISHED_UPDATE) == true) + { + unpublishedUpdates = true; + publishInProgress = ((Boolean)nodeService.getProperty(actionDefNodeRef, RecordsManagementModel.PROP_PUBLISH_IN_PROGRESS)).booleanValue(); + } + + actions.add(createActionDefModel(actionDef, actionsUrl + "/" + actionDef.getId())); + } + scheduleModel.put("actions", actions); + scheduleModel.put("unpublishedUpdates", unpublishedUpdates); + scheduleModel.put("publishInProgress", publishInProgress); + + // create model object with just the schedule data + Map model = new HashMap(1); + model.put("schedule", scheduleModel); + return model; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/DodCustomTypesGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/DodCustomTypesGet.java new file mode 100644 index 0000000000..f35ebd0319 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/DodCustomTypesGet.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.dod5015.DOD5015Model; +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.QName; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * This class provides the implementation for the dodcustomtypes.get webscript. + * + * @author Neil McErlean + */ +public class DodCustomTypesGet extends DeclarativeWebScript +{ + // TODO Investigate a way of not hard-coding the 4 custom types here. + private final static List customTypeAspects = Arrays.asList(new QName[]{DOD5015Model.ASPECT_SCANNED_RECORD, + DOD5015Model.ASPECT_PDF_RECORD, DOD5015Model.ASPECT_DIGITAL_PHOTOGRAPH_RECORD, DOD5015Model.ASPECT_WEB_RECORD}); + + private DictionaryService dictionaryService; + + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + @Override + public Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + Map model = new HashMap(); + + List customTypeAspectDefinitions = new ArrayList(4); + for (QName aspectQName : customTypeAspects) + { + AspectDefinition nextAspectDef = dictionaryService.getAspect(aspectQName); + customTypeAspectDefinitions.add(nextAspectDef); + } + model.put("dodCustomTypes", customTypeAspectDefinitions); + + return model; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/EmailMapGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/EmailMapGet.java new file mode 100644 index 0000000000..7f225e5cac --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/EmailMapGet.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.alfresco.module.org_alfresco_module_rm.email.CustomEmailMappingService; +import org.alfresco.module.org_alfresco_module_rm.email.CustomMapping; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * Implementation for Java backed webscript to return + * custom email field mappings + */ +public class EmailMapGet extends DeclarativeWebScript +{ + + /* + * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.Status, org.alfresco.web.scripts.Cache) + */ + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + // String requestUrl = req.getURL(); + + Set emailMap = customEmailMappingService.getCustomMappings(); + + // create model object with the lists model + Map model = new HashMap(1); + model.put("emailmap", emailMap); + return model; + } + + private CustomEmailMappingService customEmailMappingService; + + public void setCustomEmailMappingService(CustomEmailMappingService customEmailMappingService) + { + this.customEmailMappingService = customEmailMappingService; + } + + public CustomEmailMappingService getCustomEmailMappingService() + { + return customEmailMappingService; + } + +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/EmailMapPost.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/EmailMapPost.java new file mode 100644 index 0000000000..a712cc2e64 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/EmailMapPost.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.alfresco.module.org_alfresco_module_rm.email.CustomEmailMappingService; +import org.alfresco.module.org_alfresco_module_rm.email.CustomMapping; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * Implementation for Java backed webscript to return + * custom email field mappings + */ +public class EmailMapPost extends DeclarativeWebScript +{ + /* + * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.Status, org.alfresco.web.scripts.Cache) + */ + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + + try + { + JSONObject json = null; + json = new JSONObject(new JSONTokener(req.getContent().getContent())); + + if(json.has("add")) + { + JSONArray toAdd = json.getJSONArray("add"); + for(int i = 0 ; i < toAdd.length(); i++) + { + JSONObject val = toAdd.getJSONObject(i); + customEmailMappingService.addCustomMapping(val.getString("from"), val.getString("to")); + + } + } + + if(json.has("delete")) + { + JSONArray toDelete = json.getJSONArray("delete"); + for(int i = 0 ; i < toDelete.length(); i++) + { + JSONObject val = toDelete.getJSONObject(i); + customEmailMappingService.deleteCustomMapping(val.getString("from"), val.getString("to")); + } + } + + + // Set the return value. + Set emailMap = customEmailMappingService.getCustomMappings(); + // create model object with the lists model + Map model = new HashMap(1); + model.put("emailmap", emailMap); + return model; + } + catch (IOException iox) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not read content from req.", iox); + } + catch (JSONException je) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not parse JSON from req.", je); + } + } + + private CustomEmailMappingService customEmailMappingService; + + public void setCustomEmailMappingService(CustomEmailMappingService customEmailMappingService) + { + this.customEmailMappingService = customEmailMappingService; + } + + public CustomEmailMappingService getCustomEmailMappingService() + { + return customEmailMappingService; + } + + +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/EmailMapPut.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/EmailMapPut.java new file mode 100644 index 0000000000..f49a8bfa9b --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/EmailMapPut.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.alfresco.module.org_alfresco_module_rm.email.CustomEmailMappingService; +import org.alfresco.module.org_alfresco_module_rm.email.CustomMapping; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * Implementation for Java backed webscript to return + * custom email field mappings + */ +public class EmailMapPut extends DeclarativeWebScript +{ + /* + * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.Status, org.alfresco.web.scripts.Cache) + */ + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + + try + { + JSONObject json = null; + json = new JSONObject(new JSONTokener(req.getContent().getContent())); + + if(json.has("add")) + { + JSONArray toAdd = json.getJSONArray("add"); + for(int i = 0 ; i < toAdd.length(); i++) + { + JSONObject val = toAdd.getJSONObject(i); + customEmailMappingService.addCustomMapping(val.getString("from"), val.getString("to")); + + } + } + + if(json.has("delete")) + { + JSONArray toDelete = json.getJSONArray("delete"); + for(int i = 0 ; i < toDelete.length(); i++) + { + JSONObject val = toDelete.getJSONObject(i); + customEmailMappingService.deleteCustomMapping(val.getString("from"), val.getString("to")); + } + } + + + // Set the return value. + Set emailMap = customEmailMappingService.getCustomMappings(); + // create model object with the lists model + Map model = new HashMap(1); + model.put("emailmap", emailMap); + return model; + } + catch (IOException iox) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not read content from req.", iox); + } + catch (JSONException je) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not parse JSON from req.", je); + } + } + + private CustomEmailMappingService customEmailMappingService; + + public void setCustomEmailMappingService(CustomEmailMappingService customEmailMappingService) + { + this.customEmailMappingService = customEmailMappingService; + } + + public CustomEmailMappingService getCustomEmailMappingService() + { + return customEmailMappingService; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/ExportPost.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/ExportPost.java new file mode 100644 index 0000000000..b4b4c09303 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/ExportPost.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.io.PrintWriter; + +import org.alfresco.model.ContentModel; +import org.alfresco.model.RenditionModel; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementSearchBehaviour; +import org.alfresco.repo.exporter.ACPExportPackageHandler; +import org.alfresco.repo.web.scripts.content.StreamACP; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.view.ExporterCrawlerParameters; +import org.alfresco.service.cmr.view.Location; +import org.alfresco.service.namespace.QName; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.springframework.extensions.webscripts.WebScriptResponse; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; + +/** + * Creates an RM specific ACP file of nodes to export then streams it back + * to the client. + * + * @author Gavin Cornwell + */ +public class ExportPost extends StreamACP +{ + /** Logger */ + private static Log logger = LogFactory.getLog(ExportPost.class); + + protected static final String PARAM_TRANSFER_FORMAT = "transferFormat"; + + /** + * @see org.alfresco.web.scripts.WebScript#execute(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse) + */ + @SuppressWarnings("deprecation") + @Override + public void execute(WebScriptRequest req, WebScriptResponse res) throws IOException + { + File tempACPFile = null; + try + { + NodeRef[] nodeRefs = null; + boolean transferFormat = false; + String contentType = req.getContentType(); + if (MULTIPART_FORMDATA.equals(contentType)) + { + // get nodeRefs parameter from form + nodeRefs = getNodeRefs(req.getParameter(PARAM_NODE_REFS)); + + // look for the transfer format + String transferFormatParam = req.getParameter(PARAM_TRANSFER_FORMAT); + if (transferFormatParam != null && transferFormatParam.length() > 0) + { + transferFormat = Boolean.parseBoolean(transferFormatParam); + } + } + else + { + // presume the request is a JSON request so get nodeRefs from JSON body + JSONObject json = new JSONObject(new JSONTokener(req.getContent().getContent())); + nodeRefs = getNodeRefs(json); + + if (json.has(PARAM_TRANSFER_FORMAT)) + { + transferFormat = json.getBoolean(PARAM_TRANSFER_FORMAT); + } + } + + // setup the ACP parameters + ExporterCrawlerParameters params = new ExporterCrawlerParameters(); + params.setCrawlSelf(true); + params.setCrawlChildNodes(true); + params.setExportFrom(new Location(nodeRefs)); + + // if transfer format has been requested we need to exclude certain aspects + if (transferFormat) + { + // restrict specific aspects from being returned + QName[] excludedAspects = new QName[] { + RenditionModel.ASPECT_RENDITIONED, + ContentModel.ASPECT_THUMBNAILED, + RecordsManagementModel.ASPECT_DISPOSITION_LIFECYCLE, + RecordsManagementSearchBehaviour.ASPECT_RM_SEARCH}; + params.setExcludeAspects(excludedAspects); + } + + // create an ACP of the nodes + tempACPFile = createACP(params, + transferFormat ? ZIP_EXTENSION : ACPExportPackageHandler.ACP_EXTENSION, + transferFormat); + + // stream the ACP back to the client as an attachment (forcing save as) + streamContent(req, res, tempACPFile, true, tempACPFile.getName()); + } + catch (IOException ioe) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not read content from req.", ioe); + } + catch (JSONException je) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not parse JSON from req.", je); + } + catch(Throwable e) + { + if (logger.isDebugEnabled()) + { + StringWriter stack = new StringWriter(); + e.printStackTrace(new PrintWriter(stack)); + logger.debug("Caught exception; decorating with appropriate status template : " + stack.toString()); + } + + throw createStatusException(e, req, res); + } + finally + { + // try and delete the temporary file + if (tempACPFile != null) + { + if (logger.isDebugEnabled()) + logger.debug("Deleting temporary archive: " + tempACPFile.getAbsolutePath()); + + tempACPFile.delete(); + } + } + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/ImportPost.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/ImportPost.java new file mode 100644 index 0000000000..dfe1a2c8de --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/ImportPost.java @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService; +import org.alfresco.repo.exporter.ACPExportPackageHandler; +import org.alfresco.repo.importer.ACPImportPackageHandler; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.view.ImporterService; +import org.alfresco.service.cmr.view.Location; +import org.alfresco.util.TempFileProvider; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.springframework.extensions.webscripts.WrappingWebScriptRequest; +import org.springframework.extensions.webscripts.servlet.WebScriptServletRequest; +import org.springframework.extensions.webscripts.servlet.FormData.FormField; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.util.FileCopyUtils; + +/** + * Imports an ACP file into a records management container. + * + * @author Gavin Cornwell + */ +public class ImportPost extends DeclarativeWebScript +{ + /** Logger */ + private static Log logger = LogFactory.getLog(ImportPost.class); + + protected static final String MULTIPART_FORMDATA = "multipart/form-data"; + protected static final String PARAM_DESTINATION = "destination"; + protected static final String PARAM_ARCHIVE = "archive"; + protected static final String TEMP_FILE_PREFIX = "import_"; + + protected NodeService nodeService; + protected DictionaryService dictionaryService; + protected ImporterService importerService; + protected RecordsManagementService rmService; + protected RecordsManagementSecurityService rmSecurityService; + + /** + * @param nodeService + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Sets the data dictionary service + * + * @param dictionaryService The DictionaryService instance + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + /** + * Sets the ImporterService to use + * + * @param importerService The ImporterService + */ + public void setImporterService(ImporterService importerService) + { + this.importerService = importerService; + } + + /** + * Sets the RecordsManagementSecurityService instance + * + * @param rmSecurityService The RecordsManagementSecurityService instance + */ + public void setRecordsManagementSecurityService(RecordsManagementSecurityService rmSecurityService) + { + this.rmSecurityService = rmSecurityService; + } + + /** + * Sets the RecordsManagementService instance + * + * @param rmService The RecordsManagementService instance + */ + public void setRecordsManagementService(RecordsManagementService rmService) + { + this.rmService = rmService; + } + + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + // Unwrap to a WebScriptServletRequest if we have one + WebScriptServletRequest webScriptServletRequest = null; + WebScriptRequest current = req; + do + { + if (current instanceof WebScriptServletRequest) + { + webScriptServletRequest = (WebScriptServletRequest) current; + current = null; + } + else if (current instanceof WrappingWebScriptRequest) + { + current = ((WrappingWebScriptRequest) req).getNext(); + } + else + { + current = null; + } + } + while (current != null); + + // get the content type of request and ensure it's multipart/form-data + String contentType = req.getContentType(); + if (MULTIPART_FORMDATA.equals(contentType) && webScriptServletRequest != null) + { + String nodeRef = req.getParameter(PARAM_DESTINATION); + + if (nodeRef == null || nodeRef.length() == 0) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Mandatory 'destination' parameter was not provided in form data"); + } + + // create and check noderef + final NodeRef destination = new NodeRef(nodeRef); + if (nodeService.exists(destination)) + { + // check the destination is an RM container + if (!nodeService.hasAspect(destination, RecordsManagementModel.ASPECT_FILE_PLAN_COMPONENT) || + !dictionaryService.isSubClass(nodeService.getType(destination), ContentModel.TYPE_FOLDER)) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "NodeRef '" + destination + "' does not represent an Records Management container node."); + } + } + else + { + status.setCode(HttpServletResponse.SC_NOT_FOUND, + "NodeRef '" + destination + "' does not exist."); + } + + // as there is no 'import capability' and the RM admin user is different from + // the DM admin user (meaning the webscript 'admin' authentication can't be used) + // perform a manual check here to ensure the current user has the RM admin role. + boolean isAdmin = this.rmSecurityService.hasRMAdminRole( + this.rmService.getFilePlan(destination), + AuthenticationUtil.getRunAsUser()); + if (!isAdmin) + { + throw new WebScriptException(Status.STATUS_FORBIDDEN, "Access Denied"); + } + + File acpFile = null; + try + { + // create a temporary file representing uploaded ACP file + FormField acpContent = webScriptServletRequest.getFileField(PARAM_ARCHIVE); + if (acpContent == null) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Mandatory 'archive' file content was not provided in form data"); + } + + acpFile = TempFileProvider.createTempFile(TEMP_FILE_PREFIX, "." + ACPExportPackageHandler.ACP_EXTENSION); + + // copy contents of uploaded file to temp ACP file + FileOutputStream fos = new FileOutputStream(acpFile); + FileCopyUtils.copy(acpContent.getInputStream(), fos); // NOTE: this method closes both streams + + if (logger.isDebugEnabled()) + logger.debug("Importing uploaded ACP (" + acpFile.getAbsolutePath() + ") into " + nodeRef); + + // setup the import handler + final ACPImportPackageHandler importHandler = new ACPImportPackageHandler(acpFile, "UTF-8"); + + // import the ACP file as the system user + AuthenticationUtil.runAs(new RunAsWork() + { + public NodeRef doWork() throws Exception + { + importerService.importView(importHandler, new Location(destination), null, null); + return null; + } + }, AuthenticationUtil.getSystemUserName()); + + // create and return model + Map model = new HashMap(1); + model.put("success", true); + return model; + } + catch (FileNotFoundException fnfe) + { + throw new WebScriptException(Status.STATUS_INTERNAL_SERVER_ERROR, + "Failed to import ACP file", fnfe); + } + catch (IOException ioe) + { + throw new WebScriptException(Status.STATUS_INTERNAL_SERVER_ERROR, + "Failed to import ACP file", ioe); + } + finally + { + if (acpFile != null) + { + acpFile.delete(); + } + } + } + else + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Request is not " + MULTIPART_FORMDATA + " encoded"); + } + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/ListOfValuesGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/ListOfValuesGet.java new file mode 100644 index 0000000000..c313aded0c --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/ListOfValuesGet.java @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; +import org.alfresco.module.org_alfresco_module_rm.audit.AuditEvent; +import org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditService; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEvent; +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.repository.Period; +import org.alfresco.service.cmr.repository.PeriodProvider; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.springframework.util.StringUtils; + +/** + * Implementation for Java backed webscript to return lists + * of values for various records management services. + * + * @author Gavin Cornwell + */ +public class ListOfValuesGet extends DeclarativeWebScript +{ + protected RecordsManagementService rmService; + protected RecordsManagementActionService rmActionService; + protected RecordsManagementAuditService rmAuditService; + protected RecordsManagementEventService rmEventService; + protected DispositionService dispositionService; + protected DictionaryService ddService; + protected NamespaceService namespaceService; + + /** + * Sets the RecordsManagementService instance + * + * @param rmService The RecordsManagementService instance + */ + public void setRecordsManagementService(RecordsManagementService rmService) + { + this.rmService = rmService; + } + + /** + * Sets the RecordsManagementActionService instance + * + * @param rmActionService The RecordsManagementActionService instance + */ + public void setRecordsManagementActionService(RecordsManagementActionService rmActionService) + { + this.rmActionService = rmActionService; + } + + /** + * Sets the RecordsManagementAuditService instance + * + * @param rmAuditService The RecordsManagementAuditService instance + */ + public void setRecordsManagementAuditService(RecordsManagementAuditService rmAuditService) + { + this.rmAuditService = rmAuditService; + } + + /** + * Sets the RecordsManagementEventService instance + * + * @param rmEventService The RecordsManagementEventService instance + */ + public void setRecordsManagementEventService(RecordsManagementEventService rmEventService) + { + this.rmEventService = rmEventService; + } + + /** + * Sets the disposition service + * + * @param dispositionService the disposition service + */ + public void setDispositionService(DispositionService dispositionService) + { + this.dispositionService = dispositionService; + } + + /** + * Sets the DictionaryService instance + * + * @param ddService The DictionaryService instance + */ + public void setDictionaryService(DictionaryService ddService) + { + this.ddService = ddService; + } + + /** + * Sets the NamespaceService instance + * + * @param namespaceService The NamespaceService instance + */ + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + /* + * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.Status, org.alfresco.web.scripts.Cache) + */ + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + // add all the lists data to a Map + Map listsModel = new HashMap(4); + String requestUrl = req.getURL(); + listsModel.put("dispositionActions", createDispositionActionsModel(requestUrl)); + listsModel.put("events", createEventsModel(requestUrl)); + listsModel.put("periodTypes", createPeriodTypesModel(requestUrl)); + listsModel.put("periodProperties", createPeriodPropertiesModel(requestUrl)); + listsModel.put("auditEvents", createAuditEventsModel(requestUrl)); + + // create model object with the lists model + Map model = new HashMap(1); + model.put("lists", listsModel); + return model; + } + + /** + * Creates the model for the list of disposition actions. + * + * @param baseUrl The base URL of the service + * @return model of disposition actions list + */ + protected Map createDispositionActionsModel(String baseUrl) + { + // iterate over the disposition actions + List dispositionActions = this.rmActionService.getDispositionActions(); + List> items = new ArrayList>(dispositionActions.size()); + for (RecordsManagementAction dispositionAction : dispositionActions) + { + Map item = new HashMap(2); + item.put("label", dispositionAction.getLabel()); + item.put("value", dispositionAction.getName()); + items.add(item); + } + + // create the model + Map model = new HashMap(2); + model.put("url", baseUrl + "/dispositionactions"); + model.put("items", items); + + return model; + } + + /** + * Creates the model for the list of events. + * + * @param baseUrl The base URL of the service + * @return model of events list + */ + protected Map createEventsModel(String baseUrl) + { + // get all the events including their display labels from the event service + List events = this.rmEventService.getEvents(); + List> items = new ArrayList>(events.size()); + for (RecordsManagementEvent event : events) + { + Map item = new HashMap(3); + item.put("label", event.getDisplayLabel()); + item.put("value", event.getName()); + item.put("automatic", + this.rmEventService.getEventType(event.getType()).isAutomaticEvent()); + items.add(item); + } + + // create the model + Map model = new HashMap(2); + model.put("url", baseUrl + "/events"); + model.put("items", items); + + return model; + } + + /** + * Creates the model for the list of period types. + * + * @param baseUrl The base URL of the service + * @return model of period types list + */ + protected Map createPeriodTypesModel(String baseUrl) + { + // iterate over all period provides, but ignore 'cron' + Set providers = Period.getProviderNames(); + List> items = new ArrayList>(providers.size()); + for (String provider : providers) + { + PeriodProvider pp = Period.getProvider(provider); + if (!pp.getPeriodType().equals("cron")) + { + Map item = new HashMap(2); + item.put("label", pp.getDisplayLabel()); + item.put("value", pp.getPeriodType()); + items.add(item); + } + } + + // create the model + Map model = new HashMap(2); + model.put("url", baseUrl + "/periodtypes"); + model.put("items", items); + + return model; + } + + /** + * Creates the model for the list of period properties. + * + * @param baseUrl The base URL of the service + * @return model of period properties list + */ + protected Map createPeriodPropertiesModel(String baseUrl) + { + // iterate over all period properties and get the label from their type definition + List periodProperties = dispositionService.getDispositionPeriodProperties(); + List> items = new ArrayList>(periodProperties.size()); + for (QName periodProperty : periodProperties) + { + PropertyDefinition propDef = this.ddService.getProperty(periodProperty); + + if (propDef != null) + { + Map item = new HashMap(2); + String propTitle = propDef.getTitle(); + if (propTitle == null || propTitle.length() == 0) + { + propTitle = StringUtils.capitalize(periodProperty.getLocalName()); + } + item.put("label", propTitle); + item.put("value", periodProperty.toPrefixString(this.namespaceService)); + items.add(item); + } + } + + // create the model + Map model = new HashMap(2); + model.put("url", baseUrl + "/periodproperties"); + model.put("items", items); + + return model; + } + + /** + * Creates the model for the list of audit events. + * + * @param baseUrl The base URL of the service + * @return model of audit events list + */ + protected Map createAuditEventsModel(String baseUrl) + { + // iterate over all audit events + List auditEvents = this.rmAuditService.getAuditEvents(); + List> items = new ArrayList>(auditEvents.size()); + for (AuditEvent event : auditEvents) + { + Map item = new HashMap(2); + item.put("label", event.getLabel()); + item.put("value", event.getName()); + items.add(item); + } + + // create the model + Map model = new HashMap(2); + model.put("url", baseUrl + "/auditevents"); + model.put("items", items); + + return model; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/RMConstraintGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/RMConstraintGet.java new file mode 100644 index 0000000000..dc8cb88530 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/RMConstraintGet.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.caveat.RMCaveatConfigService; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * Implementation for Java backed webscript to return + * the values for an RM constraint. + */ +public class RMConstraintGet extends DeclarativeWebScript +{ + + /* + * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.Status, org.alfresco.web.scripts.Cache) + */ + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + String requestUrl = req.getURL(); + String extensionPath = req.getExtensionPath(); + + String constraintName = extensionPath.replace('_', ':'); + + List values = caveatConfigService.getRMAllowedValues(constraintName); + + // create model object with the lists model + Map model = new HashMap(1); + model.put("allowedValuesForCurrentUser", values); + model.put("constraintName", extensionPath); + + return model; + } + + public void setCaveatConfigService(RMCaveatConfigService caveatConfigService) + { + this.caveatConfigService = caveatConfigService; + } + + public RMCaveatConfigService getCaveatConfigService() + { + return caveatConfigService; + } + + private RMCaveatConfigService caveatConfigService; + +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/RecordMetaDataAspectsGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/RecordMetaDataAspectsGet.java new file mode 100644 index 0000000000..a15ac40173 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/RecordMetaDataAspectsGet.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * @author Roy Wetherall + */ +public class RecordMetaDataAspectsGet extends DeclarativeWebScript +{ + protected DictionaryService dictionaryService; + protected NamespaceService namespaceService; + protected RecordsManagementService recordsManagementService; + + /** + * Set the dictionary service instance + * + * @param dictionaryService the {@link DictionaryService} instance + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + /** + * Sets the {@link NamespaceService} instance + * + * @param namespaceService The {@link NamespaceService} instance + */ + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + public void setRecordsManagementService(RecordsManagementService recordsManagementService) + { + this.recordsManagementService = recordsManagementService; + } + + /* + * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.Status, org.alfresco.web.scripts.Cache) + */ + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + // Get the details of all the aspects + Set aspectQNames = recordsManagementService.getRecordMetaDataAspects(); + List> aspects = new ArrayList>(aspectQNames.size()+1); + for (QName aspectQName : aspectQNames) + { + // Get the prefix aspect and default the label to the localname + String prefixString = aspectQName.toPrefixString(namespaceService); + String label = aspectQName.getLocalName(); + + Map aspect = new HashMap(2); + aspect.put("id", prefixString); + + // Try and get the aspect definition + AspectDefinition aspectDefinition = dictionaryService.getAspect(aspectQName); + if (aspectDefinition != null) + { + // Fet the label from the aspect definition + label = aspectDefinition.getTitle(); + } + aspect.put("value", label); + + // Add the aspect details to the aspects list + aspects.add(aspect); + } + + // create model object with the lists model + Map model = new HashMap(1); + model.put("aspects", aspects); + return model; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/RmActionPost.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/RmActionPost.java new file mode 100644 index 0000000000..616410a690 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/RmActionPost.java @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionResult; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.springframework.extensions.surf.util.ISO8601DateFormat; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; + +/** + * This class provides the implementation for the rmaction webscript. + * + * @author Neil McErlean + */ +public class RmActionPost extends DeclarativeWebScript +{ + private static Log logger = LogFactory.getLog(RmActionPost.class); + + private static final String PARAM_NAME = "name"; + private static final String PARAM_NODE_REF = "nodeRef"; + private static final String PARAM_NODE_REFS = "nodeRefs"; + private static final String PARAM_PARAMS = "params"; + + private NodeService nodeService; + private RecordsManagementActionService rmActionService; + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setRecordsManagementActionService(RecordsManagementActionService rmActionService) + { + this.rmActionService = rmActionService; + } + + @SuppressWarnings("unchecked") + @Override + public Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + String reqContentAsString; + try + { + reqContentAsString = req.getContent().getContent(); + } + catch (IOException iox) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not read content from req.", iox); + } + + String actionName = null; + List targetNodeRefs = null; + Map actionParams = new HashMap(3); + + try + { + JSONObject jsonObj = new JSONObject(new JSONTokener(reqContentAsString)); + + // Get the action name + if (jsonObj.has(PARAM_NAME) == true) + { + actionName = jsonObj.getString(PARAM_NAME); + } + + // Get the target references + if (jsonObj.has(PARAM_NODE_REF) == true) + { + NodeRef nodeRef = new NodeRef(jsonObj.getString(PARAM_NODE_REF)); + targetNodeRefs = new ArrayList(1); + targetNodeRefs.add(nodeRef); + } + if (jsonObj.has(PARAM_NODE_REFS) == true) + { + JSONArray jsonArray = jsonObj.getJSONArray(PARAM_NODE_REFS); + if (jsonArray.length() != 0) + { + targetNodeRefs = new ArrayList(jsonArray.length()); + for (int i = 0; i < jsonArray.length(); i++) + { + NodeRef nodeRef = new NodeRef(jsonArray.getString(i)); + targetNodeRefs.add(nodeRef); + } + } + } + + // params are optional. + if (jsonObj.has(PARAM_PARAMS)) + { + JSONObject paramsObj = jsonObj.getJSONObject(PARAM_PARAMS); + for (Iterator iter = paramsObj.keys(); iter.hasNext(); ) + { + Object nextKey = iter.next(); + String nextKeyString = (String)nextKey; + Object nextValue = paramsObj.get(nextKeyString); + + // Check for date values + if (nextValue instanceof JSONObject) + { + if (((JSONObject)nextValue).has("iso8601") == true) + { + String dateStringValue = ((JSONObject)nextValue).getString("iso8601"); + nextValue = ISO8601DateFormat.parse(dateStringValue); + } + } + + actionParams.put(nextKeyString, (Serializable)nextValue); + } + } + } + catch (JSONException exception) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Unable to parse request JSON."); + } + + // validate input: check for mandatory params. + // Some RM actions can be posted without a nodeRef. + if (actionName == null) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "A mandatory parameter has not been provided in URL"); + } + + // Check that all the nodes provided exist and build report string + StringBuffer targetNodeRefsString = new StringBuffer(30); + boolean firstTime = true; + for (NodeRef targetNodeRef : targetNodeRefs) + { + if (nodeService.exists(targetNodeRef) == false) + { + throw new WebScriptException(Status.STATUS_NOT_FOUND, + "The targetNode does not exist (" + targetNodeRef.toString() + ")"); + } + + // Build the string + if (firstTime == true) + { + firstTime = false; + } + else + { + targetNodeRefsString.append(", "); + } + targetNodeRefsString.append(targetNodeRef.toString()); + } + + // Proceed to execute the specified action on the specified node. + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Executing Record Action ") + .append(actionName) + .append(", (") + .append(targetNodeRefsString.toString()) + .append("), ") + .append(actionParams); + logger.debug(msg.toString()); + } + + Map model = new HashMap(); + if (targetNodeRefs.isEmpty()) + { + RecordsManagementActionResult result = this.rmActionService.executeRecordsManagementAction(actionName, actionParams); + if (result.getValue() != null) + { + model.put("result", result.getValue().toString()); + } + } + else + { + Map resultMap = this.rmActionService.executeRecordsManagementAction(targetNodeRefs, actionName, actionParams); + Map results = new HashMap(resultMap.size()); + for (NodeRef nodeRef : resultMap.keySet()) + { + Object value = resultMap.get(nodeRef).getValue(); + if (value != null) + { + results.put(nodeRef.toString(), resultMap.get(nodeRef).getValue().toString()); + } + } + model.put("results", results); + } + + model.put("message", "Successfully queued action [" + actionName + "] on " + targetNodeRefsString.toString()); + + return model; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/TransferGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/TransferGet.java new file mode 100644 index 0000000000..022ad7bbbe --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/TransferGet.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.io.File; +import java.io.IOException; + +import org.alfresco.model.ContentModel; +import org.alfresco.model.RenditionModel; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementSearchBehaviour; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.view.ExporterCrawlerParameters; +import org.alfresco.service.cmr.view.Location; +import org.alfresco.service.namespace.QName; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.springframework.extensions.webscripts.WebScriptResponse; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Streams the nodes of a transfer object to the client in the form of an + * ACP file. + * + * @author Gavin Cornwell + */ +public class TransferGet extends BaseTransferWebScript +{ + /** Logger */ + private static Log logger = LogFactory.getLog(TransferGet.class); + + @SuppressWarnings("deprecation") + @Override + protected File executeTransfer(NodeRef transferNode, + WebScriptRequest req, WebScriptResponse res, + Status status, Cache cache) throws IOException + { + // get all 'transferred' nodes + NodeRef[] itemsToTransfer = getTransferNodes(transferNode); + + // setup the ACP parameters + ExporterCrawlerParameters params = new ExporterCrawlerParameters(); + params.setCrawlSelf(true); + params.setCrawlChildNodes(true); + params.setExportFrom(new Location(itemsToTransfer)); + QName[] excludedAspects = new QName[] { + RenditionModel.ASPECT_RENDITIONED, + ContentModel.ASPECT_THUMBNAILED, + RecordsManagementModel.ASPECT_DISPOSITION_LIFECYCLE, + RecordsManagementSearchBehaviour.ASPECT_RM_SEARCH}; + params.setExcludeAspects(excludedAspects); + + // create an archive of all the nodes to transfer + File tempFile = createACP(params, ZIP_EXTENSION, true); + + if (logger.isDebugEnabled()) + { + logger.debug("Creating transfer archive for " + itemsToTransfer.length + + " items into file: " + tempFile.getAbsolutePath()); + } + + // stream the archive back to the client as an attachment (forcing save as) + streamContent(req, res, tempFile, true, tempFile.getName()); + + // return the temp file for deletion + return tempFile; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/TransferReportGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/TransferReportGet.java new file mode 100644 index 0000000000..ed8706224a --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/TransferReportGet.java @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; +import java.util.Date; +import java.util.List; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.util.TempFileProvider; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.surf.util.ISO8601DateFormat; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.springframework.extensions.webscripts.WebScriptResponse; + +/** + * Returns a JSON representation of a transfer report. + * + * @author Gavin Cornwell + */ +public class TransferReportGet extends BaseTransferWebScript +{ + /** Logger */ + private static Log logger = LogFactory.getLog(TransferReportGet.class); + + protected static final String REPORT_FILE_PREFIX = "report_"; + protected static final String REPORT_FILE_SUFFIX = ".json"; + + protected DictionaryService ddService; + protected RecordsManagementService rmService; + protected DispositionService dispositionService; + + /** + * Sets the DictionaryService instance + * + * @param ddService The DictionaryService instance + */ + public void setDictionaryService(DictionaryService ddService) + { + this.ddService = ddService; + } + + /** + * Sets the disposition service + * + * @param dispositionService the disposition service + */ + public void setDispositionService(DispositionService dispositionService) + { + this.dispositionService = dispositionService; + } + + /** + * Sets the RecordsManagementService instance + * + * @param rmService RecordsManagementService instance + */ + public void setRecordsManagementService(RecordsManagementService rmService) + { + this.rmService = rmService; + } + + @Override + protected File executeTransfer(NodeRef transferNode, + WebScriptRequest req, WebScriptResponse res, + Status status, Cache cache) throws IOException + { + // generate the report (will be in JSON format) + File report = generateJSONTransferReport(transferNode); + + // stream the report back to the client + streamContent(req, res, report, false); + + // return the file for deletion + return report; + } + + /** + * Generates a File containing the JSON representation of a transfer report. + * + * @param transferNode The transfer node + * @return File containing JSON representation of a transfer report + * @throws IOException + */ + File generateJSONTransferReport(NodeRef transferNode) throws IOException + { + File report = TempFileProvider.createTempFile(REPORT_FILE_PREFIX, REPORT_FILE_SUFFIX); + Writer writer = null; + try + { + // get all 'transferred' nodes + NodeRef[] itemsToTransfer = getTransferNodes(transferNode); + + if (logger.isDebugEnabled()) + { + logger.debug("Generating JSON transfer report for " + itemsToTransfer.length + + " items into file: " + report.getAbsolutePath()); + } + + // create the writer + writer = new FileWriter(report); + + // use RMService to get disposition authority + String dispositionAuthority = null; + if (itemsToTransfer.length > 0) + { + // use the first transfer item to get to disposition schedule + DispositionSchedule ds = dispositionService.getDispositionSchedule(itemsToTransfer[0]); + if (ds != null) + { + dispositionAuthority = ds.getDispositionAuthority(); + } + } + + // write the JSON header + writer.write("{\n\t\"data\":\n\t{"); + writer.write("\n\t\t\"transferDate\": \""); + writer.write(ISO8601DateFormat.format( + (Date)this.nodeService.getProperty(transferNode, ContentModel.PROP_CREATED))); + writer.write("\",\n\t\t\"transferPerformedBy\": \""); + writer.write(AuthenticationUtil.getRunAsUser()); + writer.write("\",\n\t\t\"dispositionAuthority\": \""); + writer.write(dispositionAuthority != null ? dispositionAuthority : ""); + writer.write("\",\n\t\t\"items\":\n\t\t["); + + // write out JSON representation of items to transfer + generateTransferItemsJSON(writer, itemsToTransfer); + + // write the JSON footer + writer.write("\n\t\t]\n\t}\n}"); + } + finally + { + if (writer != null) + { + try { writer.close(); } catch (IOException ioe) {} + } + } + + return report; + } + + /** + * Generates the JSON to represent the given NodeRefs + * + * @param writer Writer to write to + * @param itemsToTransfer NodeRefs being transferred + * @throws IOException + */ + protected void generateTransferItemsJSON(Writer writer, NodeRef[] itemsToTransfer) + throws IOException + { + boolean first = true; + for (NodeRef item : itemsToTransfer) + { + if (first) + { + first = false; + } + else + { + writer.write(","); + } + + if (ddService.isSubClass(nodeService.getType(item), ContentModel.TYPE_FOLDER)) + { + generateTransferFolderJSON(writer, item); + } + else + { + generateTransferRecordJSON(writer, item); + } + } + } + + /** + * Generates the JSON to represent the given folder. + * + * @param writer Writer to write to + * @param folderNode Folder being transferred + * @throws IOException + */ + protected void generateTransferFolderJSON(Writer writer, NodeRef folderNode) + throws IOException + { + // TODO: Add identation + + writer.write("\n{\n\"type\":\"folder\",\n"); + writer.write("\"name\":\""); + writer.write((String)nodeService.getProperty(folderNode, ContentModel.PROP_NAME)); + writer.write("\",\n\"nodeRef\":\""); + writer.write(folderNode.toString()); + writer.write("\",\n\"id\":\""); + writer.write((String)nodeService.getProperty(folderNode, RecordsManagementModel.PROP_IDENTIFIER)); + writer.write("\",\n\"children\":\n["); + + boolean first = true; + List assocs = this.nodeService.getChildAssocs(folderNode, + ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef child : assocs) + { + if (first) + { + first = false; + } + else + { + writer.write(","); + } + + NodeRef childRef = child.getChildRef(); + if (ddService.isSubClass(nodeService.getType(childRef), ContentModel.TYPE_FOLDER)) + { + generateTransferFolderJSON(writer, childRef); + } + else + { + generateTransferRecordJSON(writer, childRef); + } + } + + writer.write("\n]\n}"); + } + + /** + * Generates the JSON to represent the given record. + * + * @param writer Writer to write to + * @param recordNode Record being transferred + * @throws IOException + */ + protected void generateTransferRecordJSON(Writer writer, NodeRef recordNode) + throws IOException + { + writer.write("\n{\n\"type\":\"record\",\n"); + writer.write("\"name\":\""); + writer.write((String)nodeService.getProperty(recordNode, ContentModel.PROP_NAME)); + writer.write("\",\n\"nodeRef\":\""); + writer.write(recordNode.toString()); + writer.write("\",\n\"id\":\""); + writer.write((String)nodeService.getProperty(recordNode, RecordsManagementModel.PROP_IDENTIFIER)); + writer.write("\""); + + if (this.nodeService.hasAspect(recordNode, RecordsManagementModel.ASPECT_DECLARED_RECORD)) + { + writer.write(",\n\"declaredBy\":\""); + writer.write((String)nodeService.getProperty(recordNode, RecordsManagementModel.PROP_DECLARED_BY)); + writer.write("\",\n\"declaredAt\":\""); + writer.write(ISO8601DateFormat.format( + (Date)this.nodeService.getProperty(recordNode, RecordsManagementModel.PROP_DECLARED_AT))); + writer.write("\""); + } + + writer.write("\n}"); + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/TransferReportPost.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/TransferReportPost.java new file mode 100644 index 0000000000..f95dbfa4e9 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/TransferReportPost.java @@ -0,0 +1,428 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Serializable; +import java.io.Writer; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.util.TempFileProvider; +import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; +import org.springframework.extensions.surf.util.ParameterCheck; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.springframework.extensions.webscripts.WebScriptResponse; + +/** + * Files a transfer report as a record. + * + * @author Gavin Cornwell + */ +public class TransferReportPost extends BaseTransferWebScript +{ + /** Logger */ + private static Log logger = LogFactory.getLog(TransferReportPost.class); + + protected static final String REPORT_FILE_PREFIX = "report_"; + protected static final String REPORT_FILE_SUFFIX = ".html"; + protected static final String PARAM_DESTINATION = "destination"; + protected static final String RESPONSE_SUCCESS = "success"; + protected static final String RESPONSE_RECORD = "record"; + protected static final String RESPONSE_RECORD_NAME = "recordName"; + protected static final String FILE_ACTION = "file"; + + protected DictionaryService ddService; + protected RecordsManagementActionService rmActionService; + protected RecordsManagementService rmService; + protected DispositionService dispositionService; + + /** + * Sets the DictionaryService instance + * + * @param ddService The DictionaryService instance + */ + public void setDictionaryService(DictionaryService ddService) + { + this.ddService = ddService; + } + + /** + * Sets the RecordsManagementService instance + * + * @param rmService RecordsManagementService instance + */ + public void setRecordsManagementService(RecordsManagementService rmService) + { + this.rmService = rmService; + } + + /** + * Sets the disposition service + * + * @param dispositionService disposition service + */ + public void setDispositionService(DispositionService dispositionService) + { + this.dispositionService = dispositionService; + } + + /** + * Sets the RecordsManagementActionService instance + * + * @param rmActionService RecordsManagementActionService instance + */ + public void setRecordsManagementActionService(RecordsManagementActionService rmActionService) + { + this.rmActionService = rmActionService; + } + + @Override + protected File executeTransfer(NodeRef transferNode, + WebScriptRequest req, WebScriptResponse res, + Status status, Cache cache) throws IOException + { + File report = null; + + // retrieve requested format + String format = req.getFormat(); + Map model = new HashMap(); + model.put("status", status); + model.put("cache", cache); + + try + { + // extract the destination parameter, ensure it's present and it is + // a record folder + JSONObject json = new JSONObject(new JSONTokener(req.getContent().getContent())); + if (!json.has(PARAM_DESTINATION)) + { + status.setCode(HttpServletResponse.SC_BAD_REQUEST, + "Mandatory '" + PARAM_DESTINATION + "' parameter has not been supplied"); + Map templateModel = createTemplateParameters(req, res, model); + sendStatus(req, res, status, cache, format, templateModel); + return null; + } + + String destinationParam = json.getString(PARAM_DESTINATION); + NodeRef destination = new NodeRef(destinationParam); + + if (!this.nodeService.exists(destination)) + { + status.setCode(HttpServletResponse.SC_NOT_FOUND, + "Node " + destination.toString() + " does not exist"); + Map templateModel = createTemplateParameters(req, res, model); + sendStatus(req, res, status, cache, format, templateModel); + return null; + } + + // ensure the node is a filePlan object + if (!RecordsManagementModel.TYPE_RECORD_FOLDER.equals(this.nodeService.getType(destination))) + { + status.setCode(HttpServletResponse.SC_BAD_REQUEST, + "Node " + destination.toString() + " is not a record folder"); + Map templateModel = createTemplateParameters(req, res, model); + sendStatus(req, res, status, cache, format, templateModel); + return null; + } + + if (logger.isDebugEnabled()) + logger.debug("Filing transfer report as record in record folder: " + destination); + + // generate the report (will be in JSON format) + report = generateHTMLTransferReport(transferNode); + + // file the report as a record + NodeRef record = fileTransferReport(report, destination); + + if (logger.isDebugEnabled()) + logger.debug("Filed transfer report as new record: " + record); + + // return success flag and record noderef as JSON + JSONObject responseJSON = new JSONObject(); + responseJSON.put(RESPONSE_SUCCESS, (record != null)); + if (record != null) + { + responseJSON.put(RESPONSE_RECORD, record.toString()); + responseJSON.put(RESPONSE_RECORD_NAME, + (String)nodeService.getProperty(record, ContentModel.PROP_NAME)); + } + + // setup response + String jsonString = responseJSON.toString(); + res.setContentType(MimetypeMap.MIMETYPE_JSON); + res.setContentEncoding("UTF-8"); + res.setHeader("Content-Length", Long.toString(jsonString.length())); + + // write the JSON response + res.getWriter().write(jsonString); + } + catch (JSONException je) + { + throw createStatusException(je, req, res); + } + + // return the file for deletion + return report; + } + + /** + * Generates a File containing the JSON representation of a transfer report. + * + * @param transferNode The transfer node + * @return File containing JSON representation of a transfer report + * @throws IOException + */ + File generateHTMLTransferReport(NodeRef transferNode) throws IOException + { + File report = TempFileProvider.createTempFile(REPORT_FILE_PREFIX, REPORT_FILE_SUFFIX); + Writer writer = null; + try + { + // get all 'transferred' nodes + NodeRef[] itemsToTransfer = getTransferNodes(transferNode); + + if (logger.isDebugEnabled()) + { + logger.debug("Generating HTML transfer report for " + itemsToTransfer.length + + " items into file: " + report.getAbsolutePath()); + } + + // create the writer + writer = new FileWriter(report); + + // use RMService to get disposition authority + String dispositionAuthority = null; + if (itemsToTransfer.length > 0) + { + // use the first transfer item to get to disposition schedule + DispositionSchedule ds = dispositionService.getDispositionSchedule(itemsToTransfer[0]); + if (ds != null) + { + dispositionAuthority = ds.getDispositionAuthority(); + } + } + + // write the HTML header + writer.write("\n"); + writer.write("\n\n"); + writer.write("Transfer Report\n"); + writer.write("\n"); + writer.write("\n

Transfer Report

\n"); + + writer.write(""); + writer.write(""); + writer.write(""); + writer.write(""); + writer.write("
Transfer Date:"); + Date transferDate = (Date)this.nodeService.getProperty(transferNode, ContentModel.PROP_CREATED); + writer.write(StringEscapeUtils.escapeHtml(transferDate.toString())); + writer.write("
Transfer Location:"); + writer.write(StringEscapeUtils.escapeHtml((String)this.nodeService.getProperty(transferNode, + RecordsManagementModel.PROP_TRANSFER_LOCATION))); + writer.write("
Performed By:"); + writer.write(StringEscapeUtils.escapeHtml((String)this.nodeService.getProperty(transferNode, + ContentModel.PROP_CREATOR))); + writer.write("
Disposition Authority:"); + writer.write(dispositionAuthority != null ? StringEscapeUtils.escapeHtml(dispositionAuthority) : ""); + writer.write("
\n"); + + writer.write("

Transferred Items

\n"); + + // write out HTML representation of items to transfer + generateTransferItemsHTML(writer, itemsToTransfer); + + // write the HTML footer + writer.write(""); + } + finally + { + if (writer != null) + { + try { writer.close(); } catch (IOException ioe) {} + } + } + + return report; + } + + /** + * Generates the JSON to represent the given NodeRefs + * + * @param writer Writer to write to + * @param itemsToTransfer NodeRefs being transferred + * @throws IOException + */ + protected void generateTransferItemsHTML(Writer writer, NodeRef[] itemsToTransfer) + throws IOException + { + for (NodeRef item : itemsToTransfer) + { + writer.write("
\n"); + if (ddService.isSubClass(nodeService.getType(item), ContentModel.TYPE_FOLDER)) + { + generateTransferFolderHTML(writer, item); + } + else + { + generateTransferRecordHTML(writer, item); + } + writer.write("
\n"); + } + } + + /** + * Generates the JSON to represent the given folder. + * + * @param writer Writer to write to + * @param folderNode Folder being transferred + * @throws IOException + */ + protected void generateTransferFolderHTML(Writer writer, NodeRef folderNode) + throws IOException + { + writer.write(""); + writer.write(StringEscapeUtils.escapeHtml((String)this.nodeService.getProperty(folderNode, + ContentModel.PROP_NAME))); + writer.write(" (Unique Folder Identifier: "); + writer.write(StringEscapeUtils.escapeHtml((String)this.nodeService.getProperty(folderNode, + RecordsManagementModel.PROP_IDENTIFIER))); + writer.write(")\n"); + + writer.write("
\n"); + + // NOTE: we don't expect any nested folder structures so just render + // the records contained in the folder. + + List assocs = this.nodeService.getChildAssocs(folderNode, + ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef child : assocs) + { + NodeRef childRef = child.getChildRef(); + if (this.nodeService.hasAspect(childRef, RecordsManagementModel.ASPECT_RECORD)) + { + generateTransferRecordHTML(writer, childRef); + } + } + + writer.write("\n
\n"); + } + + /** + * Generates the JSON to represent the given record. + * + * @param writer Writer to write to + * @param recordNode Record being transferred + * @throws IOException + */ + protected void generateTransferRecordHTML(Writer writer, NodeRef recordNode) + throws IOException + { + writer.write("
\n"); + writer.write(" "); + writer.write(StringEscapeUtils.escapeHtml((String)this.nodeService.getProperty(recordNode, + ContentModel.PROP_NAME))); + writer.write(" (Unique Record Identifier: "); + writer.write(StringEscapeUtils.escapeHtml((String)this.nodeService.getProperty(recordNode, + RecordsManagementModel.PROP_IDENTIFIER))); + writer.write(")"); + + if (this.nodeService.hasAspect(recordNode, RecordsManagementModel.ASPECT_DECLARED_RECORD)) + { + Date declaredOn = (Date)this.nodeService.getProperty(recordNode, RecordsManagementModel.PROP_DECLARED_AT); + writer.write(" declared by "); + writer.write(StringEscapeUtils.escapeHtml((String)this.nodeService.getProperty(recordNode, + RecordsManagementModel.PROP_DECLARED_BY))); + writer.write(" on "); + writer.write(StringEscapeUtils.escapeHtml(declaredOn.toString())); + } + + writer.write("\n
\n"); + } + + /** + * Files the given transfer report as a record in the given record folder. + * + * @param report Report to file + * @param destination The destination record folder + * @return NodeRef of the created record + */ + protected NodeRef fileTransferReport(File report, NodeRef destination) + { + ParameterCheck.mandatory("report", report); + ParameterCheck.mandatory("destination", destination); + + NodeRef record = null; + + Map properties = new HashMap(1); + properties.put(ContentModel.PROP_NAME, report.getName()); + + // file the transfer report as an undeclared record + record = this.nodeService.createNode(destination, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, + QName.createValidLocalName(report.getName())), + ContentModel.TYPE_CONTENT, properties).getChildRef(); + + // Set the content + ContentWriter writer = this.contentService.getWriter(record, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_HTML); + writer.setEncoding("UTF-8"); + writer.putContent(report); + + // file the node as a record + this.rmActionService.executeRecordsManagementAction(record, FILE_ACTION); + + return record; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/UserRightsReportGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/UserRightsReportGet.java new file mode 100644 index 0000000000..343a7d9e29 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/UserRightsReportGet.java @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService; +import org.alfresco.module.org_alfresco_module_rm.security.Role; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.service.cmr.security.PersonService; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * Implementation for Java backed webscript to return user rights report. + * + * @author Gavin Cornwell + */ +public class UserRightsReportGet extends DeclarativeWebScript +{ + protected AuthorityService authorityService; + protected PersonService personService; + protected NodeService nodeService; + protected RecordsManagementService rmService; + protected RecordsManagementSecurityService rmSecurityService; + + /** + * Sets the AuthorityService instance + * + * @param authorityService AuthorityService instance + */ + public void setAuthorityService(AuthorityService authorityService) + { + this.authorityService = authorityService; + } + + /** + * Sets the PersonService instance + * + * @param personService PersonService instance + */ + public void setPersonService(PersonService personService) + { + this.personService = personService; + } + + /** + * Sets the NodeService instance + * + * @param nodeService NodeService instance + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Sets the RecordsManagementService instance + * + * @param rmService The RecordsManagementService instance + */ + public void setRecordsManagementService(RecordsManagementService rmService) + { + this.rmService = rmService; + } + + /** + * Sets the RecordsManagementSecurityService instance + * + * @param rmSecurityService The RecordsManagementSecurityService instance + */ + public void setRecordsManagementSecurityService(RecordsManagementSecurityService rmSecurityService) + { + this.rmSecurityService = rmSecurityService; + } + + /* + * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.Status, org.alfresco.web.scripts.Cache) + */ + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + // get the RM root nodes in the system + List rmRoots = this.rmService.getFilePlans(); + + if (rmRoots == null || rmRoots.size() == 0) + { + status.setCode(HttpServletResponse.SC_BAD_REQUEST, + "There are no Records Management root nodes in the system"); + return null; + } + + // construct all the maps etc. needed to build the model + Map usersMap = new HashMap(8); + Map rolesMap = new HashMap(8); + Map groupsMap = new HashMap(8); + + // TODO: deal with presence of more than one root, for now we know it's only 1 + NodeRef rmRootNode = rmRoots.get(0); + + // iterate over all the roles for the file plan and construct models + Set roles = this.rmSecurityService.getRoles(rmRootNode); + for (Role role : roles) + { + // get or create the RoleModel object for current role + String roleName = role.getName(); + RoleModel roleModel = rolesMap.get(roleName); + if (roleModel == null) + { + roleModel = new RoleModel(role); + rolesMap.put(roleName, roleModel); + } + + // get the users for the current RM role + String group = role.getRoleGroupName(); + Set users = this.authorityService.getContainedAuthorities(AuthorityType.USER, group, true); + roleModel.setUsers(users); + + // setup a user model object for each user + for (String userName : users) + { + UserModel userModel = usersMap.get(userName); + if (userModel == null) + { + NodeRef userRef = this.personService.getPerson(userName); + userModel = new UserModel(userName, + (String)this.nodeService.getProperty(userRef, ContentModel.PROP_FIRSTNAME), + (String)this.nodeService.getProperty(userRef, ContentModel.PROP_LASTNAME)); + usersMap.put(userName, userModel); + } + + userModel.addRole(roleName); + } + + // get the groups for the cuurent RM role + Set groups = this.authorityService.getContainedAuthorities(AuthorityType.GROUP, group, false); + roleModel.setGroups(groups); + + // setup a user model object for each user in each group + for (String groupName : groups) + { + GroupModel groupModel = groupsMap.get(groupName); + if (groupModel == null) + { + groupModel = new GroupModel(groupName, + this.authorityService.getAuthorityDisplayName(groupName)); + groupsMap.put(groupName, groupModel); + } + + // get users in each group + Set groupUsers = this.authorityService.getContainedAuthorities(AuthorityType.USER, groupName, true); + for (String userName : groupUsers) + { + UserModel userModel = usersMap.get(userName); + if (userModel == null) + { + NodeRef userRef = this.personService.getPerson(userName); + userModel = new UserModel(userName, + (String)this.nodeService.getProperty(userRef, ContentModel.PROP_FIRSTNAME), + (String)this.nodeService.getProperty(userRef, ContentModel.PROP_LASTNAME)); + usersMap.put(userName, userModel); + } + + userModel.addGroup(groupName); + userModel.addRole(roleName); + groupModel.addUser(userName); + } + } + } + + // add all the lists data to a Map + Map reportModel = new HashMap(4); + reportModel.put("users", usersMap); + reportModel.put("roles", rolesMap); + reportModel.put("groups", groupsMap); + + // create model object with the lists model + Map model = new HashMap(1); + model.put("report", reportModel); + return model; + } + + /** + * Class to represent a role for use in a Freemarker template. + * + * @author Gavin Cornwell + */ + public class RoleModel extends Role + { + private Set users = new HashSet(8); + private Set groups = new HashSet(8); + + public RoleModel(Role role) + { + super(role.getName(), role.getDisplayLabel(), role.getCapabilities(), role.getRoleGroupName()); + } + + public void addUser(String username) + { + this.users.add(username); + } + + public void addGroup(String groupName) + { + this.groups.add(groupName); + } + + public void setUsers(Set users) + { + this.users = users; + } + + public void setGroups(Set groups) + { + this.groups = groups; + } + + public Set getUsers() + { + return this.users; + } + + public Set getGroups() + { + return this.groups; + } + } + + /** + * Class to represent a user for use in a Freemarker template. + * + * @author Gavin Cornwell + */ + public class UserModel + { + private String userName; + private String firstName; + private String lastName; + private Set roles; + private Set groups; + + public UserModel(String userName, String firstName, String lastName) + { + this.userName = userName; + this.firstName = firstName; + this.lastName = lastName; + this.roles = new HashSet(2); + this.groups = new HashSet(2); + } + + public String getUserName() + { + return this.userName; + } + + public String getFirstName() + { + return this.firstName; + } + + public String getLastName() + { + return this.lastName; + } + + public Set getRoles() + { + return this.roles; + } + + public Set getGroups() + { + return this.groups; + } + + public void addRole(String roleName) + { + this.roles.add(roleName); + } + + public void addGroup(String groupName) + { + this.groups.add(groupName); + } + } + + /** + * Class to represent a group for use in a Freemarker template. + * + * @author Gavin Cornwell + */ + public class GroupModel + { + private String name; + private String label; + private Set users; + + public GroupModel(String name, String label) + { + this.name = name; + this.label = label; + this.users = new HashSet(4); + } + + public String getName() + { + return this.name; + } + + public String getDisplayLabel() + { + return this.label; + } + + public Set getUsers() + { + return this.users; + } + + public void addUser(String userName) + { + this.users.add(userName); + } + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmEventDelete.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmEventDelete.java new file mode 100644 index 0000000000..e5d8b0501c --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmEventDelete.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script.admin; + +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Records management event delete web script + * + * @author Roy Wetherall + */ +public class RmEventDelete extends DeclarativeWebScript +{ + @SuppressWarnings("unused") + private static Log logger = LogFactory.getLog(RmEventDelete.class); + + /** Reccords management event service */ + private RecordsManagementEventService rmEventService; + + /** + * Set the records management event service + * + * @param rmEventService + */ + public void setRecordsManagementEventService(RecordsManagementEventService rmEventService) + { + this.rmEventService = rmEventService; + } + + /** + * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.Status, org.alfresco.web.scripts.Cache) + */ + @Override + public Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + Map model = new HashMap(); + + // Event name + Map templateVars = req.getServiceMatch().getTemplateVars(); + String eventName = templateVars.get("eventname"); + if (eventName == null) + { + throw new WebScriptException(Status.STATUS_NOT_FOUND, "No event name was provided on the URL."); + } + + // Check the event exists + if (rmEventService.existsEvent(eventName) == false) + { + throw new WebScriptException(Status.STATUS_NOT_FOUND, "The event " + eventName + " does not exist."); + } + + // Remove the event + rmEventService.removeEvent(eventName); + + return model; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmEventGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmEventGet.java new file mode 100644 index 0000000000..ad01f08864 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmEventGet.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script.admin; + +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEvent; +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Records management event GET web script + * + * @author Roy Wetherall + */ +public class RmEventGet extends DeclarativeWebScript +{ + @SuppressWarnings("unused") + private static Log logger = LogFactory.getLog(RmEventGet.class); + + /** Reccords management event service */ + private RecordsManagementEventService rmEventService; + + /** + * Set the records management event service + * + * @param rmEventService + */ + public void setRecordsManagementEventService(RecordsManagementEventService rmEventService) + { + this.rmEventService = rmEventService; + } + + @Override + public Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + Map model = new HashMap(); + + // Event name + Map templateVars = req.getServiceMatch().getTemplateVars(); + String eventName = templateVars.get("eventname"); + if (eventName == null) + { + throw new WebScriptException(Status.STATUS_NOT_FOUND, "No event name was provided on the URL."); + } + + // Check the event exists + if (rmEventService.existsEvent(eventName) == false) + { + throw new WebScriptException(Status.STATUS_NOT_FOUND, "The event " + eventName + " does not exist."); + } + + // Get the event + RecordsManagementEvent event = rmEventService.getEvent(eventName); + model.put("event", event); + + return model; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmEventPut.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmEventPut.java new file mode 100644 index 0000000000..8717870eea --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmEventPut.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script.admin; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEvent; +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; + +/** + * Records management event PUT web script + * + * @author Roy Wetherall + */ +public class RmEventPut extends DeclarativeWebScript +{ + @SuppressWarnings("unused") + private static Log logger = LogFactory.getLog(RmEventPut.class); + + /** Reccords management event service */ + private RecordsManagementEventService rmEventService; + + /** + * Set the records management event service + * + * @param rmEventService + */ + public void setRecordsManagementEventService(RecordsManagementEventService rmEventService) + { + this.rmEventService = rmEventService; + } + + @Override + public Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + Map model = new HashMap(); + JSONObject json = null; + try + { + // Event name + Map templateVars = req.getServiceMatch().getTemplateVars(); + String eventName = templateVars.get("eventname"); + if (eventName == null) + { + throw new WebScriptException(Status.STATUS_NOT_FOUND, "No event name was provided on the URL."); + } + + // Check the event exists + if (rmEventService.existsEvent(eventName) == false) + { + throw new WebScriptException(Status.STATUS_NOT_FOUND, "The event " + eventName + " does not exist."); + } + + json = new JSONObject(new JSONTokener(req.getContent().getContent())); + + String eventDisplayLabel = null; + if (json.has("eventDisplayLabel") == true) + { + eventDisplayLabel = json.getString("eventDisplayLabel"); + } + if (eventDisplayLabel == null || eventDisplayLabel.length() == 0) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "No event display label provided."); + } + + String eventType = null; + if (json.has("eventType") == true) + { + eventType = json.getString("eventType"); + } + if (eventType == null || eventType.length() == 0) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "No event type provided."); + } + + + RecordsManagementEvent event = rmEventService.addEvent(eventType, eventName, eventDisplayLabel); + model.put("event", event); + + } + catch (IOException iox) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not read content from req.", iox); + } + catch (JSONException je) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not parse JSON from req.", je); + } + + return model; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmEventTypesGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmEventTypesGet.java new file mode 100644 index 0000000000..2b9a57a1f5 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmEventTypesGet.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script.admin; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService; +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventType; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Records management event types GET web script + * + * @author Roy Wetherall + */ +public class RmEventTypesGet extends DeclarativeWebScript +{ + @SuppressWarnings("unused") + private static Log logger = LogFactory.getLog(RmEventTypesGet.class); + + /** Reccords management event service */ + private RecordsManagementEventService rmEventService; + + /** + * Set the records management event service + * + * @param rmEventService + */ + public void setRecordsManagementEventService(RecordsManagementEventService rmEventService) + { + this.rmEventService = rmEventService; + } + + @Override + public Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + Map model = new HashMap(); + + // Get the events + List events = rmEventService.getEventTypes(); + model.put("eventtypes", events); + + return model; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmEventsGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmEventsGet.java new file mode 100644 index 0000000000..91d0c96ef2 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmEventsGet.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script.admin; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEvent; +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Records management events GET web script + * + * @author Roy Wetherall + */ +public class RmEventsGet extends DeclarativeWebScript +{ + @SuppressWarnings("unused") + private static Log logger = LogFactory.getLog(RmEventsGet.class); + + /** Reccords management event service */ + private RecordsManagementEventService rmEventService; + + /** + * Set the records management event service + * + * @param rmEventService + */ + public void setRecordsManagementEventService(RecordsManagementEventService rmEventService) + { + this.rmEventService = rmEventService; + } + + @Override + public Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + Map model = new HashMap(); + + // Get the events + List events = rmEventService.getEvents(); + model.put("events", events); + + return model; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmEventsPost.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmEventsPost.java new file mode 100644 index 0000000000..02aad56389 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmEventsPost.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script.admin; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEvent; +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService; +import org.alfresco.util.GUID; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; + +/** + * + * + * @author Roy Wetherall + */ +public class RmEventsPost extends DeclarativeWebScript +{ + @SuppressWarnings("unused") + private static Log logger = LogFactory.getLog(RmEventsPost.class); + + /** Reccords management event service */ + private RecordsManagementEventService rmEventService; + + /** + * Set the records management event service + * + * @param rmEventService + */ + public void setRecordsManagementEventService(RecordsManagementEventService rmEventService) + { + this.rmEventService = rmEventService; + } + + + @Override + public Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + Map model = new HashMap(); + JSONObject json = null; + try + { + json = new JSONObject(new JSONTokener(req.getContent().getContent())); + + String eventName = null; + if (json.has("eventName") == true) + { + eventName = json.getString("eventName"); + } + + if (eventName == null || eventName.length() == 0) + { + // Generate the event name + eventName = GUID.generate(); + } + + String eventDisplayLabel = null; + if (json.has("eventDisplayLabel") == true) + { + eventDisplayLabel = json.getString("eventDisplayLabel"); + } + if (eventDisplayLabel == null || eventDisplayLabel.length() == 0) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "No event display label provided."); + } + + String eventType = null; + if (json.has("eventType") == true) + { + eventType = json.getString("eventType"); + } + if (eventType == null || eventType.length() == 0) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "No event type provided."); + } + + RecordsManagementEvent event = rmEventService.addEvent(eventType, eventName, eventDisplayLabel); + model.put("event", event); + + } + catch (IOException iox) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not read content from req.", iox); + } + catch (JSONException je) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not parse JSON from req.", je); + } + + return model; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmRoleDelete.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmRoleDelete.java new file mode 100644 index 0000000000..0f0fa50180 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmRoleDelete.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script.admin; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * + * + * @author Roy Wetherall + */ +public class RmRoleDelete extends DeclarativeWebScript +{ + @SuppressWarnings("unused") + private static Log logger = LogFactory.getLog(RmRoleDelete.class); + + private RecordsManagementService rmService; + private RecordsManagementSecurityService rmSecurityService; + + public void setRecordsManagementSecurityService(RecordsManagementSecurityService rmSecurityService) + { + this.rmSecurityService = rmSecurityService; + } + + public void setRecordsManagementService(RecordsManagementService rmService) + { + this.rmService = rmService; + } + + @Override + public Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + Map model = new HashMap(); + + // Role name + Map templateVars = req.getServiceMatch().getTemplateVars(); + String roleParam = templateVars.get("rolename"); + if (roleParam == null) + { + throw new WebScriptException(Status.STATUS_NOT_FOUND, "No role name was provided on the URL."); + } + + List roots = rmService.getFilePlans(); + NodeRef root = roots.get(0); + + // Check that the role exists + if (rmSecurityService.existsRole(root, roleParam) == false) + { + throw new WebScriptException(Status.STATUS_NOT_FOUND, "The role " + roleParam + " does not exist on the records managment root " + root); + } + + rmSecurityService.deleteRole(root, roleParam); + + return model; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmRoleGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmRoleGet.java new file mode 100644 index 0000000000..85e008cf55 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmRoleGet.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script.admin; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * + * + * @author Roy Wetherall + */ +public class RmRoleGet extends DeclarativeWebScript +{ + @SuppressWarnings("unused") + private static Log logger = LogFactory.getLog(RmRoleGet.class); + + private RecordsManagementService rmService; + private RecordsManagementSecurityService rmSecurityService; + + public void setRecordsManagementSecurityService(RecordsManagementSecurityService rmSecurityService) + { + this.rmSecurityService = rmSecurityService; + } + + public void setRecordsManagementService(RecordsManagementService rmService) + { + this.rmService = rmService; + } + + @Override + public Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + Map model = new HashMap(); + + // Role name + Map templateVars = req.getServiceMatch().getTemplateVars(); + String roleParam = templateVars.get("rolename"); + if (roleParam == null) + { + throw new WebScriptException(Status.STATUS_NOT_FOUND, "No role name was provided on the URL."); + } + + // Get the root records management node + // TODO this should be passed + List roots = rmService.getFilePlans(); + NodeRef root = roots.get(0); + + // Check that the role exists + if (rmSecurityService.existsRole(root, roleParam) == false) + { + throw new WebScriptException(Status.STATUS_NOT_FOUND, "The role " + roleParam + " does not exist on the records managment root " + root); + } + + model.put("role", rmSecurityService.getRole(root, roleParam)); + + return model; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmRolePut.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmRolePut.java new file mode 100644 index 0000000000..11429ecf5d --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmRolePut.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script.admin; + +import java.io.IOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.capability.Capability; +import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService; +import org.alfresco.module.org_alfresco_module_rm.security.Role; +import org.alfresco.service.cmr.repository.NodeRef; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; + +/** + * + * + * @author Roy Wetherall + */ +public class RmRolePut extends DeclarativeWebScript +{ + @SuppressWarnings("unused") + private static Log logger = LogFactory.getLog(RmRolePut.class); + + private RecordsManagementService rmService; + private RecordsManagementSecurityService rmSecurityService; + + public void setRecordsManagementSecurityService(RecordsManagementSecurityService rmSecurityService) + { + this.rmSecurityService = rmSecurityService; + } + + public void setRecordsManagementService(RecordsManagementService rmService) + { + this.rmService = rmService; + } + + @Override + public Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + Map model = new HashMap(); + JSONObject json = null; + try + { + // Role name + Map templateVars = req.getServiceMatch().getTemplateVars(); + String roleParam = templateVars.get("rolename"); + if (roleParam == null) + { + throw new WebScriptException(Status.STATUS_NOT_FOUND, "No role name was provided on the URL."); + } + + json = new JSONObject(new JSONTokener(req.getContent().getContent())); + String name = json.getString("name"); + // TODO check + String displayLabel = json.getString("displayLabel"); + // TODO check + + JSONArray capabilitiesArray = json.getJSONArray("capabilities"); + Set capabilites = new HashSet(capabilitiesArray.length()); + for (int i = 0; i < capabilitiesArray.length(); i++) + { + Capability capability = rmSecurityService.getCapability(capabilitiesArray.getString(i)); + capabilites.add(capability); + } + + List roots = rmService.getFilePlans(); + NodeRef root = roots.get(0); + + // Check that the role exists + if (rmSecurityService.existsRole(root, roleParam) == false) + { + throw new WebScriptException(Status.STATUS_NOT_FOUND, "The role " + roleParam + " does not exist on the records managment root " + root); + } + + Role role = rmSecurityService.updateRole(root, name, displayLabel, capabilites); + model.put("role", role); + + } + catch (IOException iox) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not read content from req.", iox); + } + catch (JSONException je) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not parse JSON from req.", je); + } + + return model; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmRolesGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmRolesGet.java new file mode 100644 index 0000000000..76d108c124 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmRolesGet.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script.admin; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService; +import org.alfresco.module.org_alfresco_module_rm.security.Role; +import org.alfresco.service.cmr.repository.NodeRef; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * + * + * @author Roy Wetherall + */ +public class RmRolesGet extends DeclarativeWebScript +{ + @SuppressWarnings("unused") + private static Log logger = LogFactory.getLog(RmRolesGet.class); + + private RecordsManagementService rmService; + private RecordsManagementSecurityService rmSecurityService; + + public void setRecordsManagementSecurityService(RecordsManagementSecurityService rmSecurityService) + { + this.rmSecurityService = rmSecurityService; + } + + public void setRecordsManagementService(RecordsManagementService rmService) + { + this.rmService = rmService; + } + + @Override + public Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + Map model = new HashMap(); + Set roles = null; + + // TODO should be passed + List roots = rmService.getFilePlans(); + NodeRef root = roots.get(0); + + // Get the user filter + String user = req.getParameter("user"); + if (user != null && user.length() != 0) + { + roles = rmSecurityService.getRolesByUser(root, user); + } + else + { + roles = rmSecurityService.getRoles(root); + } + + model.put("roles", roles); + + return model; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmRolesPost.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmRolesPost.java new file mode 100644 index 0000000000..fb3656aebd --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/admin/RmRolesPost.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script.admin; + +import java.io.IOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.capability.Capability; +import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService; +import org.alfresco.module.org_alfresco_module_rm.security.Role; +import org.alfresco.service.cmr.repository.NodeRef; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; + +/** + * + * + * @author Roy Wetherall + */ +public class RmRolesPost extends DeclarativeWebScript +{ + @SuppressWarnings("unused") + private static Log logger = LogFactory.getLog(RmRolesPost.class); + + private RecordsManagementService rmService; + private RecordsManagementSecurityService rmSecurityService; + + public void setRecordsManagementSecurityService(RecordsManagementSecurityService rmSecurityService) + { + this.rmSecurityService = rmSecurityService; + } + + public void setRecordsManagementService(RecordsManagementService rmService) + { + this.rmService = rmService; + } + + @Override + public Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + Map model = new HashMap(); + JSONObject json = null; + try + { + json = new JSONObject(new JSONTokener(req.getContent().getContent())); + String name = json.getString("name"); + // TODO check + String displayString = json.getString("displayLabel"); + // TODO check + + JSONArray capabilitiesArray = json.getJSONArray("capabilities"); + Set capabilites = new HashSet(capabilitiesArray.length()); + for (int i = 0; i < capabilitiesArray.length(); i++) + { + Capability capability = rmSecurityService.getCapability(capabilitiesArray.getString(i)); + capabilites.add(capability); + } + + List roots = rmService.getFilePlans(); + NodeRef root = roots.get(0); + + Role role = rmSecurityService.createRole(root, name, displayString, capabilites); + + Set roles = rmSecurityService.getRoles(root); + model.put("role", role); + + } + catch (IOException iox) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not read content from req.", iox); + } + catch (JSONException je) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not parse JSON from req.", je); + } + + return model; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/RMSavedSearchesDelete.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/RMSavedSearchesDelete.java new file mode 100644 index 0000000000..e0022bf527 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/RMSavedSearchesDelete.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script.slingshot; + +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchService; +import org.alfresco.service.cmr.site.SiteService; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * Records Management saved search DELETE web script + * + * @author Roy Wetherall + */ +public class RMSavedSearchesDelete extends DeclarativeWebScript +{ + /** Records management search service */ + protected RecordsManagementSearchService recordsManagementSearchService; + + /** Site service */ + protected SiteService siteService; + + /** + * @param recordsManagementSearchService records management search service + */ + public void setRecordsManagementSearchService(RecordsManagementSearchService recordsManagementSearchService) + { + this.recordsManagementSearchService = recordsManagementSearchService; + } + + /** + * @param siteService site service + */ + public void setSiteService(SiteService siteService) + { + this.siteService = siteService; + } + + /* + * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.Status, org.alfresco.web.scripts.Cache) + */ + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + Map templateVars = req.getServiceMatch().getTemplateVars(); + + // Get the site id and confirm it's valid + String siteId = templateVars.get("site"); + if (siteId == null || siteId.length() == 0) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Site id not provided."); + } + if (siteService.getSite(siteId) == null) + { + throw new WebScriptException(Status.STATUS_NOT_FOUND, "Site not found."); + } + + // Get the name of the saved search + String name = templateVars.get("name"); + if (name == null || name.length() == 0) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Saved search name not provided."); + } + + // Delete the saved search + recordsManagementSearchService.deleteSavedSearch(siteId, name); + + // Indicate success in the model + Map model = new HashMap(1); + model.put("success", true); + return model; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/RMSavedSearchesGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/RMSavedSearchesGet.java new file mode 100644 index 0000000000..aee41dcb28 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/RMSavedSearchesGet.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script.slingshot; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchService; +import org.alfresco.module.org_alfresco_module_rm.search.SavedSearchDetails; +import org.alfresco.service.cmr.site.SiteService; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * RM saved searches GET web script + * + * @author Roy Wetherall + */ +public class RMSavedSearchesGet extends DeclarativeWebScript +{ + /** Records management search service */ + protected RecordsManagementSearchService recordsManagementSearchService; + + /** Site service */ + protected SiteService siteService; + + /** + * @param recordsManagementSearchService records management search service + */ + public void setRecordsManagementSearchService(RecordsManagementSearchService recordsManagementSearchService) + { + this.recordsManagementSearchService = recordsManagementSearchService; + } + + /** + * @param siteService site service + */ + public void setSiteService(SiteService siteService) + { + this.siteService = siteService; + } + + /* + * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.Status, org.alfresco.web.scripts.Cache) + */ + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + // create model object with the lists model + Map model = new HashMap(13); + + // Get the site id and confirm it is valid + Map templateVars = req.getServiceMatch().getTemplateVars(); + String siteId = templateVars.get("site"); + if (siteId == null || siteId.length() == 0) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Site id not provided."); + } + if (siteService.getSite(siteId) == null) + { + throw new WebScriptException(Status.STATUS_NOT_FOUND, "Site not found."); + } + + // TODO determine whether this is still relevant +// String isPublicString = req.getParameter("p"); +// boolean isPublic = false; +// if (isPublicString != null && isPublicString.length() != 0) +// { +// isPublic = Boolean.parseBoolean(isPublicString); +// } + + // Get the saved search details + List details = recordsManagementSearchService.getSavedSearches(siteId); + List items = new ArrayList(); + for (SavedSearchDetails savedSearchDetails : details) + { + String name = savedSearchDetails.getName(); + String description = savedSearchDetails.getDescription(); + String query = savedSearchDetails.getCompatibility().getQuery(); + String params = savedSearchDetails.getCompatibility().getParams(); + String sort = savedSearchDetails.getCompatibility().getSort(); + + Item item = new Item(name, description, query, params, sort); + items.add(item); + } + + model.put("savedSearches", items); + return model; + } + + /** + * Item class to contain information about items being placed in model. + */ + public class Item + { + private String name; + private String description; + private String query; + private String params; + private String sort; + + public Item(String name, String description, String query, String params, String sort) + { + this.name = name; + this.description = description; + this.query = query; + this.params = params; + this.sort = sort; + } + + public String getName() + { + return name; + } + + public String getDescription() + { + return description; + } + + public String getQuery() + { + return query; + } + + public String getParams() + { + return params; + } + + public String getSort() + { + return sort; + } + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/RMSavedSearchesPost.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/RMSavedSearchesPost.java new file mode 100644 index 0000000000..2d36073c58 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/RMSavedSearchesPost.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script.slingshot; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchParameters; +import org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchService; +import org.alfresco.module.org_alfresco_module_rm.search.SavedSearchDetailsCompatibility; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.namespace.NamespaceService; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * Records management saved search POST web script. + * + * @author Roy Wetherall + */ +public class RMSavedSearchesPost extends DeclarativeWebScript +{ + /** Records management search service */ + protected RecordsManagementSearchService recordsManagementSearchService; + + /** Site service */ + protected SiteService siteService; + + /** Namespace service */ + protected NamespaceService namespaceService; + + /** + * @param recordsManagementSearchService records management search service + */ + public void setRecordsManagementSearchService(RecordsManagementSearchService recordsManagementSearchService) + { + this.recordsManagementSearchService = recordsManagementSearchService; + } + + /** + * @param siteService site service + */ + public void setSiteService(SiteService siteService) + { + this.siteService = siteService; + } + + /** + * @param namespaceService namespace service + */ + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + /** + * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.Status, org.alfresco.web.scripts.Cache) + */ + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + // Get the site id and confirm it is valid + Map templateVars = req.getServiceMatch().getTemplateVars(); + String siteId = templateVars.get("site"); + if (siteId == null || siteId.length() == 0) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Site id not provided."); + } + if (siteService.getSite(siteId) == null) + { + throw new WebScriptException(Status.STATUS_NOT_FOUND, "Site not found."); + } + + // Example format of posted Saved Search JSON: + // { + // "name": "search name", + // "description": "the search description", + // "query": "the complete search query string", + // "public": boolean, + // "params": "terms=keywords:xyz&undeclared=true", + // "sort": "cm:name/asc" + //} + + try + { + // Parse the JSON passed in the request + JSONObject json = new JSONObject(new JSONTokener(req.getContent().getContent())); + + // Get the details of the saved search + if (json.has("name") == false) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Mandatory 'name' parameter was not provided in request body"); + } + String name = json.getString("name"); + String description = null; + if (json.has("description") == true) + { + description = json.getString("description"); + } + boolean isPublic = true; + if (json.has("public") == true) + { + isPublic = json.getBoolean("public"); + } + // NOTE: we do not need to worry about the query + if (json.has("params") == false) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Mandatory 'params' parameter was not provided in request body"); + } + String params = json.getString("params"); + String sort = null; + if (json.has("sort") == true) + { + sort = json.getString("sort"); + } + + // Use the compatibility class to create a saved search details and save + String search = SavedSearchDetailsCompatibility.getSearchFromParams(params); + if (search == null) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Mandatory 'terms' was not provided in 'params' parameter found in the request body"); + } + RecordsManagementSearchParameters searchParameters = SavedSearchDetailsCompatibility.createSearchParameters(params, sort, namespaceService); + recordsManagementSearchService.saveSearch(siteId, name, description, search, searchParameters, isPublic); + + } + catch (IOException iox) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not read content from req.", iox); + } + catch (JSONException je) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Could not parse JSON from req.", je); + } + + // Indicate success in the model + Map model = new HashMap(1); + model.put("success", true); + return model; + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/RMSearchGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/RMSearchGet.java new file mode 100644 index 0000000000..ef5a3face2 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/RMSearchGet.java @@ -0,0 +1,445 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.script.slingshot; + +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchParameters; +import org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchService; +import org.alfresco.module.org_alfresco_module_rm.search.SavedSearchDetailsCompatibility; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * RM search GET web script + * + * @author Roy Wetherall + */ +public class RMSearchGet extends DeclarativeWebScript +{ + /** URL Parameters */ + private static final String PARAM_QUERY = "query"; + private static final String PARAM_SORTBY = "sortby"; + private static final String PARAM_FILTERS = "filters"; + private static final String PARAM_MAX_ITEMS = "maxitems"; + + /** Records management search service */ + protected RecordsManagementSearchService recordsManagementSearchService; + + /** Site service */ + protected SiteService siteService; + + /** Namespace service */ + protected NamespaceService namespaceService; + + /** Node serivce */ + protected NodeService nodeService; + + /** Dictionary service */ + protected DictionaryService dictionaryService; + + /** Permission service */ + protected PermissionService permissionService; + + /** Person service */ + protected PersonService personService; + + /** Content service */ + protected ContentService contentService; + + /** Person data cache */ + private Map personDataCache = null; + + /** + * @param recordsManagementSearchService records management search service + */ + public void setRecordsManagementSearchService(RecordsManagementSearchService recordsManagementSearchService) + { + this.recordsManagementSearchService = recordsManagementSearchService; + } + + /** + * @param siteService site service + */ + public void setSiteService(SiteService siteService) + { + this.siteService = siteService; + } + + /** + * @param namespaceService namespace service + */ + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + /** + * @param nodeService node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @param dictionaryService dictionary service + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + /** + * @param permissionService permission service + */ + public void setPermissionService(PermissionService permissionService) + { + this.permissionService = permissionService; + } + + /** + * @param personService person service + */ + public void setPersonService(PersonService personService) + { + this.personService = personService; + } + + /** + * @param contentService content service + */ + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + + /* + * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.Status, org.alfresco.web.scripts.Cache) + */ + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + // Get the site id and confirm it is valid + Map templateVars = req.getServiceMatch().getTemplateVars(); + String siteId = templateVars.get("site"); + if (siteId == null || siteId.length() == 0) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Site id not provided."); + } + if (siteService.getSite(siteId) == null) + { + throw new WebScriptException(Status.STATUS_NOT_FOUND, "Site not found."); + } + + // Get the query parameter + String query = req.getParameter(PARAM_QUERY); + // TODO check that this is there + + String sortby = req.getParameter(PARAM_SORTBY); + // TODO this is optional + + String filters = req.getParameter(PARAM_FILTERS); + // TODO this is optional + + // Convert into a rm search parameter object + RecordsManagementSearchParameters searchParameters = + SavedSearchDetailsCompatibility.createSearchParameters(filters, new String[]{",", "/"}, sortby, namespaceService); + + // Set the max results + String maxItems = req.getParameter(PARAM_MAX_ITEMS); + if (maxItems != null && maxItems.length() != 0) + { + searchParameters.setMaxItems(Integer.parseInt(maxItems)); + } + + // Execute search + List results = recordsManagementSearchService.search(siteId, query, searchParameters); + + // Reset person data cache + personDataCache = new HashMap(57); + + // Process the result items + Item[] items = new Item[results.size()]; + int index = 0; + for (NodeRef nodeRef : results) + { + items[index] = new Item(nodeRef); + index++; + } + + // Return model + Map model = new HashMap(1); + model.put("items", items); + return model; + + } + + /** + * Item class to contain information about items being placed in model. + */ + public class Item + { + private NodeRef nodeRef; + private String type; + private int size; + private String parentFolder = ""; + private String browseUrl; + private boolean isContainer; + private String modifiedBy; + private String createdBy; + private Map nodeProperties; + private Map properties; + + public Item(NodeRef nodeRef) + { + // Set node ref + this.nodeRef = nodeRef; + + // Get type + QName nodeRefType = nodeService.getType(nodeRef); + this.type = nodeRefType.toPrefixString(namespaceService); + + // Get properties + this.nodeProperties = nodeService.getProperties(nodeRef); + + // Determine if container or not + isContainer = true; + if (dictionaryService.isSubClass(nodeRefType, ContentModel.TYPE_CONTENT) == true) + { + isContainer = false; + } + + // Get parent node reference + NodeRef parent = null; + ChildAssociationRef assoc = nodeService.getPrimaryParent(nodeRef); + if (assoc != null) + { + parent = assoc.getParentRef(); + } + + if (isContainer == true) + { + this.size = -1; + + String displayPath = nodeService.getPath(nodeRef).toDisplayPath(nodeService, permissionService); + String[] pathElements = displayPath.split("/"); + if (pathElements.length >= 5) + { + if (pathElements.length > 5) + { + this.parentFolder = (String)nodeService.getProperty(parent, ContentModel.PROP_NAME); + } + + pathElements = (String[])ArrayUtils.subarray(pathElements, 5, pathElements.length); + String newPath = StringUtils.join(pathElements, "/"); + StringBuilder relPath = new StringBuilder("/").append(newPath); + if (relPath.length() > 1) + { + relPath.append("/"); + } + relPath.append(getName()); + try + { + this.browseUrl = "documentlibrary?path=" + URLEncoder.encode(relPath.toString(), "UTF-8"); + } + catch (UnsupportedEncodingException e) + { + throw new AlfrescoRuntimeException("Could not process search results.", e); + } + } + } + else + { + // Get the document size + ContentData contentData = (ContentData)nodeProperties.get(ContentModel.PROP_CONTENT); + this.size = 0; + if (contentData != null) + { + this.size = (int)contentData.getSize(); + } + + // Set the document parent name + if (parent != null) + { + this.parentFolder = (String)nodeService.getProperty(parent, ContentModel.PROP_NAME); + } + + // Set the document browse URL + this.browseUrl = "document-details?nodeRef=" + nodeRef.toString(); + } + + this.modifiedBy = getDisplayName(getModifiedByUser()); + this.createdBy = getDisplayName(getCreatedByUser()); + + // Process the custom properties + properties = new HashMap(nodeProperties.size()); + for (Map.Entry entry : nodeProperties.entrySet()) + { + QName qName = entry.getKey().getPrefixedQName(namespaceService); + if (RecordsManagementModel.RM_URI.equals(qName.getNamespaceURI()) == true || + RecordsManagementModel.RM_CUSTOM_URI.equals(qName.getNamespaceURI()) == true) + { + String prefixName = qName.getPrefixString().replace(":", "_"); + Serializable value = entry.getValue(); + if (value instanceof NodeRef) + { + value = value.toString(); + } + else if (value instanceof ContentData) + { + ContentReader contentReader = contentService.getReader(nodeRef, qName); + value = contentReader.getContentString(); + } + properties.put(prefixName, entry.getValue()); + } + } + } + + private String getDisplayName(String userName) + { + String result = personDataCache.get(userName); + if (result == null) + { + NodeRef person = personService.getPerson(userName); + if (person != null) + { + StringBuffer displayName = new StringBuffer(128); + displayName.append(nodeService.getProperty(person, ContentModel.PROP_FIRSTNAME)) + .append(" ") + .append(nodeService.getProperty(person, ContentModel.PROP_LASTNAME)); + result = displayName.toString(); + } + else + { + result = userName; + } + personDataCache.put(userName, result); + } + + return result; + } + + public NodeRef getNodeRef() + { + return nodeRef; + } + + public String getType() + { + return type; + } + + public String getName() + { + return (String)nodeProperties.get(ContentModel.PROP_NAME); + } + + public String getTitle() + { + return (String)nodeProperties.get(ContentModel.PROP_TITLE); + } + + public String getDescription() + { + return (String)nodeProperties.get(ContentModel.PROP_DESCRIPTION); + } + + public Date getModifiedOn() + { + return (Date)nodeProperties.get(ContentModel.PROP_MODIFIED); + } + + public String getModifiedByUser() + { + return (String)nodeProperties.get(ContentModel.PROP_MODIFIER); + } + + public String getModifiedBy() + { + return modifiedBy; + } + + public Date getCreatedOn() + { + return (Date)nodeProperties.get(ContentModel.PROP_CREATED); + } + + public String getCreatedByUser() + { + return (String)nodeProperties.get(ContentModel.PROP_CREATOR); + } + + public String getCreatedBy() + { + return createdBy; + } + + public String getAuthor() + { + return (String)nodeProperties.get(ContentModel.PROP_AUTHOR); + } + + public String getParentFolder() + { + return parentFolder; + } + + public int getSize() + { + return size; + } + + public String getBrowseUrl() + { + return browseUrl; + } + + public Map getProperties() + { + return properties; + } + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/search/RecordsManagementSearchParameters.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/search/RecordsManagementSearchParameters.java new file mode 100644 index 0000000000..4529833148 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/search/RecordsManagementSearchParameters.java @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.search; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * @author Roy Wetherall + */ +@SuppressWarnings("serial") +public class RecordsManagementSearchParameters +{ + /** Default sort order */ + private static final Map DEFAULT_SORT_ORDER = new HashMap() + { + { + put(ContentModel.PROP_NAME, Boolean.TRUE); + } + }; + + /** Default templates */ + private static final Map DEFAULT_TEMPLATES = new HashMap() + { + { + put("keywords", "%(cm:name cm:title cm:description TEXT)"); + put("name", "%(cm:name)"); + put("title", "%(cm:title)"); + put("description", "%(cm:description)"); + put("creator", "%(cm:creator)"); + put("created", "%(cm:created)"); + put("modifier", "%(cm:modifier)"); + put("modified", "%(cm:modified)"); + put("author", "%(cm:author)"); + put("markings", "%(rmc:supplementalMarkingList)"); + put("dispositionEvents", "%(rma:recordSearchDispositionEvents)"); + put("dispositionActionName", "%(rma:recordSearchDispositionActionName)"); + put("dispositionActionAsOf", "%(rma:recordSearchDispositionActionAsOf)"); + put("dispositionEventsEligible", "%(rma:recordSearchDispositionEventsEligible)"); + put("dispositionPeriod", "%(rma:recordSearchDispositionPeriod)"); + put("hasDispositionSchedule", "%(rma:recordSearchHasDispositionSchedule)"); + put("dispositionInstructions", "%(rma:recordSearchDispositionInstructions)"); + put("dispositionAuthority", "%(rma:recordSearchDispositionAuthority)"); + put("holdReason", "%(rma:recordSearchHoldReason)"); + put("vitalRecordReviewPeriod", "%(rma:recordSearchVitalRecordReviewPeriod)"); + } + }; + + /** Default included container types */ + private static final List DEFAULT_INCLUDED_CONTAINER_TYPES = Collections.emptyList(); + + /** Max items */ + private int maxItems = 500; + + private boolean includeRecords = true; + private boolean includeUndeclaredRecords = false; + private boolean includeVitalRecords = false; + private boolean includeRecordFolders = true; + private boolean includeFrozen = false; + private boolean includeCutoff = false; + + private List includedContainerTypes = DEFAULT_INCLUDED_CONTAINER_TYPES; + private Map sortOrder = DEFAULT_SORT_ORDER; + private Map templates = DEFAULT_TEMPLATES; + + private static final String JSON_MAXITEMS = "maxitems"; + private static final String JSON_RECORDS = "records"; + private static final String JSON_UNDECLAREDRECORDS = "undeclaredrecords"; + private static final String JSON_VITALRECORDS = "vitalrecords"; + private static final String JSON_RECORDFOLDERES = "recordfolders"; + private static final String JSON_FROZEN = "frozen"; + private static final String JSON_CUTOFF = "cutoff"; + private static final String JSON_CONTAINERTYPES = "containertypes"; + private static final String JSON_SORT = "sort"; + private static final String JSON_FIELD = "field"; + private static final String JSON_ASCENDING = "ascending"; + + /** + * { + * "maxItems" : 500, + * "records" : true, + * "undeclaredrecords" : false, + * "vitalrecords" : false, + * "recordfolders" : false, + * "frozen" : false, + * "cutoff" : false, + * "containertypes" : + * [ + * "rma:recordSeries", + * "rma:recordCategory" + * ] + * "sort" : + * [ + * { + * "field" : "cm:name", + * "ascending" : true + * } + * ] + * } + */ + public static RecordsManagementSearchParameters createFromJSON(String json, NamespaceService namespaceService) + { + try + { + JSONObject jsonObject = new JSONObject(json); + return RecordsManagementSearchParameters.createFromJSON(jsonObject, namespaceService); + } + catch (JSONException e) + { + throw new AlfrescoRuntimeException("Unable to create records management search parameters from json string. " + json, e); + } + } + + /** + * + * @param jsonObject + * @return + */ + public static RecordsManagementSearchParameters createFromJSON(JSONObject jsonObject, NamespaceService namespaceService) + { + try + { + RecordsManagementSearchParameters searchParameters = new RecordsManagementSearchParameters(); + + // Get the search parameter properties + if (jsonObject.has(JSON_MAXITEMS) == true) + { + searchParameters.setMaxItems(jsonObject.getInt(JSON_MAXITEMS)); + } + if (jsonObject.has(JSON_RECORDS) == true) + { + searchParameters.setIncludeRecords(jsonObject.getBoolean(JSON_RECORDS)); + } + if (jsonObject.has(JSON_UNDECLAREDRECORDS) == true) + { + searchParameters.setIncludeUndeclaredRecords(jsonObject.getBoolean(JSON_UNDECLAREDRECORDS)); + } + if (jsonObject.has(JSON_VITALRECORDS) == true) + { + searchParameters.setIncludeVitalRecords(jsonObject.getBoolean(JSON_VITALRECORDS)); + } + if (jsonObject.has(JSON_RECORDFOLDERES) == true) + { + searchParameters.setIncludeRecordFolders(jsonObject.getBoolean(JSON_RECORDFOLDERES)); + } + if (jsonObject.has(JSON_FROZEN) == true) + { + searchParameters.setIncludeFrozen(jsonObject.getBoolean(JSON_FROZEN)); + } + if (jsonObject.has(JSON_CUTOFF) == true) + { + searchParameters.setIncludeCutoff(jsonObject.getBoolean(JSON_CUTOFF)); + } + + // Get container types + if (jsonObject.has(JSON_CONTAINERTYPES) == true) + { + JSONArray jsonArray = jsonObject.getJSONArray(JSON_CONTAINERTYPES); + List containerTypes = new ArrayList(jsonArray.length()); + for (int i = 0; i < jsonArray.length(); i++) + { + String type = jsonArray.getString(i); + containerTypes.add(QName.createQName(type, namespaceService)); + } + searchParameters.setIncludedContainerTypes(containerTypes); + } + + // Get sort details + if (jsonObject.has(JSON_SORT) == true) + { + JSONArray jsonArray = jsonObject.getJSONArray(JSON_SORT); + Map sortOrder = new HashMap(jsonArray.length()); + for (int i = 0; i < jsonArray.length(); i++) + { + JSONObject sortJSONObject = jsonArray.getJSONObject(i); + if (sortJSONObject.has(JSON_FIELD) == true && + sortJSONObject.has(JSON_ASCENDING) == true) + { + sortOrder.put( + QName.createQName(sortJSONObject.getString(JSON_FIELD), namespaceService), + Boolean.valueOf(sortJSONObject.getBoolean(JSON_ASCENDING))); + } + } + searchParameters.setSortOrder(sortOrder); + } + + return searchParameters; + } + catch (JSONException e) + { + throw new AlfrescoRuntimeException("Unable to create records management search parameters from json string. " + jsonObject.toString(), e); + } + } + + /** + * + * @return + */ + public String toJSONString(NamespaceService namespaceService) + { + return toJSONObject(namespaceService).toString(); + } + + public JSONObject toJSONObject(NamespaceService namespaceService) + { + try + { + JSONObject jsonObject = new JSONObject(); + jsonObject.put(JSON_MAXITEMS, maxItems); + jsonObject.put(JSON_RECORDS, includeRecords); + jsonObject.put(JSON_UNDECLAREDRECORDS, includeUndeclaredRecords); + jsonObject.put(JSON_VITALRECORDS, includeVitalRecords); + jsonObject.put(JSON_RECORDFOLDERES, includeRecordFolders); + jsonObject.put(JSON_FROZEN, includeFrozen); + jsonObject.put(JSON_CUTOFF, includeCutoff); + + // Included containers + JSONArray jsonArray = new JSONArray(); + for (QName containerType : includedContainerTypes) + { + jsonArray.put(containerType.toPrefixString(namespaceService)); + } + jsonObject.put(JSON_CONTAINERTYPES, jsonArray); + + // Sort + JSONArray jsonSortArray = new JSONArray(); + for (Map.Entry entry : sortOrder.entrySet()) + { + JSONObject jsonEntry = new JSONObject(); + jsonEntry.put(JSON_FIELD, entry.getKey().toPrefixString(namespaceService)); + jsonEntry.put(JSON_ASCENDING, entry.getValue().booleanValue()); + jsonSortArray.put(jsonEntry); + } + jsonObject.put(JSON_SORT, jsonSortArray); + + return jsonObject; + } + catch (JSONException e) + { + throw new AlfrescoRuntimeException("Unable to generate json string for records management search parameters.", e); + } + } + + public void setMaxItems(int maxItems) + { + this.maxItems = maxItems; + } + + public int getMaxItems() + { + return maxItems; + } + + public void setSortOrder(Map sortOrder) + { + this.sortOrder = sortOrder; + } + + public Map getSortOrder() + { + return sortOrder; + } + + public void setTemplates(Map templates) + { + this.templates = templates; + } + + public Map getTemplates() + { + return templates; + } + + public void setIncludeRecords(boolean includeRecords) + { + this.includeRecords = includeRecords; + } + + public boolean isIncludeRecords() + { + return includeRecords; + } + + public void setIncludeUndeclaredRecords(boolean includeUndeclaredRecords) + { + this.includeUndeclaredRecords = includeUndeclaredRecords; + } + + public boolean isIncludeUndeclaredRecords() + { + return includeUndeclaredRecords; + } + + public void setIncludeVitalRecords(boolean includeVitalRecords) + { + this.includeVitalRecords = includeVitalRecords; + } + + public boolean isIncludeVitalRecords() + { + return includeVitalRecords; + } + + public void setIncludeRecordFolders(boolean includeRecordFolders) + { + this.includeRecordFolders = includeRecordFolders; + } + + public boolean isIncludeRecordFolders() + { + return includeRecordFolders; + } + + public void setIncludeFrozen(boolean includeFrozen) + { + this.includeFrozen = includeFrozen; + } + + public boolean isIncludeFrozen() + { + return includeFrozen; + } + + public void setIncludeCutoff(boolean includeCutoff) + { + this.includeCutoff = includeCutoff; + } + + public boolean isIncludeCutoff() + { + return includeCutoff; + } + + public void setIncludedContainerTypes(List includedContainerTypes) + { + this.includedContainerTypes = includedContainerTypes; + } + + public List getIncludedContainerTypes() + { + return includedContainerTypes; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/search/RecordsManagementSearchService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/search/RecordsManagementSearchService.java new file mode 100644 index 0000000000..b4e971bae5 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/search/RecordsManagementSearchService.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.search; + +import java.util.List; + +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Records management search service. + * + * @author Roy Wetherall + */ +public interface RecordsManagementSearchService +{ + /** + * Execute a records management search + * @param siteId the id of the rm site to query + * @param query search query string + * @param searchParameters search parameters + * @return {@link List}<{@link NodeRef}> search results + */ + List search(String siteId, String query, RecordsManagementSearchParameters searchParameters); + + /** + * Get all the searches saved on the given records management site. + * @param siteId site id + * @return {@link List<{@link SavedSearchDetails}>} list of saved search details + */ + List getSavedSearches(String siteId); + + /** + * Get a named saved search for a given records management site. + * @param siteId site id + * @param name name of search + * @return {@link SavedSearchDetails} saved search details + */ + SavedSearchDetails getSavedSearch(String siteId, String name); + + /** + * Save records management search. + * @param siteId site id + * @param name name + * @param description description + * @param search search string + * @param isPublic indicates whether the saved search is public or not + * @return {@link SavedSearchDetails} details of the saved search + */ + SavedSearchDetails saveSearch(String siteId, String name, String description, String search, RecordsManagementSearchParameters searchParameters, boolean isPublic); + + /** + * Save records management search. + * @param savedSearchDetails details of search to save + * @return {@link SavedSearchDetails} details of the saved search + */ + SavedSearchDetails saveSearch(SavedSearchDetails savedSearchDetails); + + /** + * Delete saved search + * @param siteId site id + * @param name name of saved search + */ + void deleteSavedSearch(String siteId, String name); + + /** + * Delete saved search + * @param savedSearchDetails saved search details + */ + void deleteSavedSearch(SavedSearchDetails savedSearchDetails); + + /** + * Adds the reports as saved searches to a given site. + * @param siteId site id + */ + void addReports(String siteId); +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/search/RecordsManagementSearchServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/search/RecordsManagementSearchServiceImpl.java new file mode 100644 index 0000000000..73254dd6e4 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/search/RecordsManagementSearchServiceImpl.java @@ -0,0 +1,612 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.search; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ISO9075; +import org.alfresco.util.ParameterCheck; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Records management search service implementation + * + * @author Roy Wetherall + */ +public class RecordsManagementSearchServiceImpl implements RecordsManagementSearchService +{ + private static final String SITES_SPACE_QNAME_PATH = "/app:company_home/st:sites/"; + + /** Name of the main site container used to store the saved searches within */ + private static final String SEARCH_CONTAINER = "Saved Searches"; + + /** File folder service */ + private FileFolderService fileFolderService; + + /** Search service */ + private SearchService searchService; + + /** Site service */ + private SiteService siteService; + + /** Namespace service */ + private NamespaceService namespaceService; + + /** List of report details */ + private List reports = new ArrayList(13); + + /** + * @param fileFolderService file folder service + */ + public void setFileFolderService(FileFolderService fileFolderService) + { + this.fileFolderService = fileFolderService; + } + + /** + * @param searchService search service + */ + public void setSearchService(SearchService searchService) + { + this.searchService = searchService; + } + + /** + * @param siteService site service + */ + public void setSiteService(SiteService siteService) + { + this.siteService = siteService; + } + + /** + * @param namespaceService namespace service + */ + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + /** + * @param reportsJSON + */ + public void setReportsJSON(String reportsJSON) + { + try + { + JSONArray jsonArray = new JSONArray(reportsJSON); + if (jsonArray != null) + { + for (int i=0; i < jsonArray.length(); i++) + { + JSONObject report = jsonArray.getJSONObject(i); + + // Get the name + if (report.has(SavedSearchDetails.NAME) == false) + { + throw new AlfrescoRuntimeException("Unable to load report details because name has not been specified. \n" + reportsJSON); + } + String name = report.getString(SavedSearchDetails.NAME); + + // Get the query + if (report.has(SavedSearchDetails.SEARCH) == false) + { + throw new AlfrescoRuntimeException("Unable to load report details because search has not been specified for report " + name + ". \n" + reportsJSON); + } + String query = report.getString(SavedSearchDetails.SEARCH); + + // Get the description + String description = ""; + if (report.has(SavedSearchDetails.DESCRIPTION) == true) + { + description = report.getString(SavedSearchDetails.DESCRIPTION); + } + + RecordsManagementSearchParameters searchParameters = new RecordsManagementSearchParameters(); + if (report.has("searchparams") == true) + { + searchParameters = RecordsManagementSearchParameters.createFromJSON(report.getJSONObject("searchparams"), namespaceService); + } + + // Create the report details and add to list + ReportDetails reportDetails = new ReportDetails(name, description, query, searchParameters); + reports.add(reportDetails); + } + } + } + catch (JSONException exception) + { + throw new AlfrescoRuntimeException("Unable to load report details.\n" + reportsJSON, exception); + } + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchService#search(java.lang.String, java.lang.String, org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchParameters) + */ + @Override + public List search(String siteId, String query, RecordsManagementSearchParameters rmSearchParameters) + { + // build the full RM query + StringBuilder fullQuery = new StringBuilder(1024); + fullQuery.append("PATH:\"") + .append(SITES_SPACE_QNAME_PATH) + .append("cm:").append(ISO9075.encode(siteId)).append("/cm:documentLibrary//*\"") + .append(" AND (") + .append(buildQueryString(query, rmSearchParameters)) + .append(")"); + + // create the search parameters + SearchParameters searchParameters = new SearchParameters(); + searchParameters.setQuery(fullQuery.toString()); + searchParameters.setLanguage(SearchService.LANGUAGE_FTS_ALFRESCO); + searchParameters.addStore(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); + searchParameters.setMaxItems(rmSearchParameters.getMaxItems()); + searchParameters.setNamespace(RecordsManagementModel.RM_URI); + + // set sort + for(Entry entry : rmSearchParameters.getSortOrder().entrySet()) + { + searchParameters.addSort(entry.getKey().toPrefixString(namespaceService), entry.getValue().booleanValue()); + } + + // set templates + for (Entry entry : rmSearchParameters.getTemplates().entrySet()) + { + searchParameters.addQueryTemplate(entry.getKey(), entry.getValue()); + } + + // execute query + ResultSet resultSet = searchService.query(searchParameters); + + // return results + return resultSet.getNodeRefs(); + } + + /** + * + * @param queryTerm + * @param aspects + * @param types + * @return + */ + /*package*/ String buildQueryString(String queryTerm, RecordsManagementSearchParameters searchParameters) + { + StringBuilder aspectQuery = new StringBuilder(); + if (searchParameters.isIncludeRecords() == true) + { + appendAspect(aspectQuery, "rma:record"); + if (searchParameters.isIncludeUndeclaredRecords() == false) + { + appendAspect(aspectQuery, "rma:declaredRecord"); + } + if (searchParameters.isIncludeVitalRecords() == true) + { + appendAspect(aspectQuery, "rma:vitalRecord"); + } + } + + StringBuilder typeQuery = new StringBuilder(); + if (searchParameters.isIncludeRecordFolders() == true) + { + appendType(typeQuery, "rma:recordFolder"); + } + List includedContainerTypes = searchParameters.getIncludedContainerTypes(); + if (includedContainerTypes != null && includedContainerTypes.size() != 0) + { + for (QName includedContainerType : includedContainerTypes) + { + appendType(typeQuery, includedContainerType.toPrefixString(namespaceService)); + } + } + + StringBuilder query = new StringBuilder(); + if (queryTerm == null || queryTerm.length() == 0) + { + // Default to search for everything + query.append("ISNODE:T"); + } + else + { + if (isComplexQueryTerm(queryTerm) == true) + { + query.append(queryTerm); + } + else + { + query.append("keywords:\"" + queryTerm + "\""); + } + } + + StringBuilder fullQuery = new StringBuilder(1024); + if (aspectQuery.length() != 0 || typeQuery.length() != 0) + { + if (aspectQuery.length() != 0 && typeQuery.length() != 0) + { + fullQuery.append("("); + } + + if (aspectQuery.length() != 0) + { + fullQuery.append("(").append(aspectQuery).append(") "); + } + + if (typeQuery.length() != 0) + { + fullQuery.append("(").append(typeQuery).append(")"); + } + + if (aspectQuery.length() != 0 && typeQuery.length() != 0) + { + fullQuery.append(")"); + } + } + + if (searchParameters.isIncludeFrozen() == true) + { + appendAspect(fullQuery, "rma:frozen"); + } + else + { + appendNotAspect(fullQuery, "rma:frozen"); + } + if (searchParameters.isIncludeCutoff() == true) + { + appendAspect(fullQuery, "rma:cutOff"); + } + + if (fullQuery.length() != 0) + { + fullQuery.append(" AND "); + } + fullQuery.append("(") + .append(query) + .append(") AND NOT ASPECT:\"rma:versionedRecord\""); + + return fullQuery.toString(); + } + + private boolean isComplexQueryTerm(String query) + { + return query.matches(".*[\":].*"); + } + + /** + * + * @param sb + * @param aspect + */ + private void appendAspect(StringBuilder sb, String aspect) + { + appendWithJoin(sb, " AND ", "ASPECT:\"", aspect, "\""); + } + + private void appendNotAspect(StringBuilder sb, String aspect) + { + appendWithJoin(sb, " AND ", "NOT ASPECT:\"", aspect, "\""); + } + + /** + * + * @param sb + * @param type + */ + private void appendType(StringBuilder sb, String type) + { + appendWithJoin(sb, " ", "TYPE:\"", type, "\""); + } + + /** + * + * @param sb + * @param withJoin + * @param prefix + * @param value + * @param postfix + */ + private void appendWithJoin(StringBuilder sb, String withJoin, String prefix, String value, String postfix) + { + if (sb.length() != 0) + { + sb.append(withJoin); + } + sb.append(prefix).append(value).append(postfix); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchService#getSavedSearches(java.lang.String) + */ + @Override + public List getSavedSearches(String siteId) + { + List result = new ArrayList(17); + + NodeRef container = siteService.getContainer(siteId, SEARCH_CONTAINER); + if (container != null) + { + // add the details of all the public saved searches + List searches = fileFolderService.listFiles(container); + for (FileInfo search : searches) + { + addSearchDetailsToList(result, search.getNodeRef()); + } + + // add the details of any "private" searches for the current user + String userName = AuthenticationUtil.getFullyAuthenticatedUser(); + NodeRef userContainer = fileFolderService.searchSimple(container, userName); + if (userContainer != null) + { + List userSearches = fileFolderService.listFiles(userContainer); + for (FileInfo userSearch : userSearches) + { + addSearchDetailsToList(result, userSearch.getNodeRef()); + } + } + } + + return result; + } + + /** + * Add the search details to the list. + * @param searches list of search details + * @param searchNode search node + */ + private void addSearchDetailsToList(List searches, NodeRef searchNode) + { + ContentReader reader = fileFolderService.getReader(searchNode); + String jsonString = reader.getContentString(); + SavedSearchDetails savedSearchDetails = SavedSearchDetails.createFromJSON(jsonString, namespaceService, this); + searches.add(savedSearchDetails); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchService#getSavedSearch(java.lang.String, java.lang.String) + */ + @Override + public SavedSearchDetails getSavedSearch(String siteId, String name) + { + // check for mandatory parameters + ParameterCheck.mandatory("siteId", siteId); + ParameterCheck.mandatory("name", name); + + SavedSearchDetails result = null; + + // get the saved search node + NodeRef searchNode = getSearchNodeRef(siteId, name); + + if (searchNode != null) + { + // get the json content + ContentReader reader = fileFolderService.getReader(searchNode); + String jsonString = reader.getContentString(); + + // create the saved search details + result = SavedSearchDetails.createFromJSON(jsonString, namespaceService, this); + } + + return result; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchService#saveSearch(java.lang.String, java.lang.String, java.lang.String, java.lang.String, boolean) + */ + @Override + public SavedSearchDetails saveSearch(String siteId, String name, String description, String query, RecordsManagementSearchParameters searchParameters, boolean isPublic) + { + // Check for mandatory parameters + ParameterCheck.mandatory("siteId", siteId); + ParameterCheck.mandatory("name", name); + ParameterCheck.mandatory("query", query); + ParameterCheck.mandatory("searchParameters", searchParameters); + + // Create saved search details + SavedSearchDetails savedSearchDetails = new SavedSearchDetails(siteId, name, description, query, searchParameters, isPublic, false, namespaceService, this); + + // Save search details + return saveSearch(savedSearchDetails); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchService#saveSearch(org.alfresco.module.org_alfresco_module_rm.search.SavedSearchDetails) + */ + @Override + public SavedSearchDetails saveSearch(final SavedSearchDetails savedSearchDetails) + { + // Check for mandatory parameters + ParameterCheck.mandatory("savedSearchDetails", savedSearchDetails); + + // Get the root saved search container + final String siteId = savedSearchDetails.getSiteId(); + NodeRef container = siteService.getContainer(siteId, SEARCH_CONTAINER); + if (container == null) + { + container = AuthenticationUtil.runAs(new RunAsWork() + { + @Override + public NodeRef doWork() throws Exception + { + return siteService.createContainer(siteId, SEARCH_CONTAINER, null, null); + } + }, AuthenticationUtil.getSystemUserName()); + } + + // Get the private container for the current user + if (savedSearchDetails.isPublic() == false) + { + final String userName = AuthenticationUtil.getFullyAuthenticatedUser(); + NodeRef userContainer = fileFolderService.searchSimple(container, userName); + if (userContainer == null) + { + final NodeRef parentContainer = container; + userContainer = AuthenticationUtil.runAs(new RunAsWork() + { + @Override + public NodeRef doWork() throws Exception + { + return fileFolderService.create(parentContainer, userName, ContentModel.TYPE_FOLDER).getNodeRef(); + } + }, AuthenticationUtil.getSystemUserName()); + } + container = userContainer; + } + + // Get the saved search node + NodeRef searchNode = fileFolderService.searchSimple(container, savedSearchDetails.getName()); + if (searchNode == null) + { + final NodeRef searchContainer = container; + searchNode = AuthenticationUtil.runAs(new RunAsWork() + { + @Override + public NodeRef doWork() throws Exception + { + return fileFolderService.create(searchContainer, savedSearchDetails.getName(), ContentModel.TYPE_CONTENT).getNodeRef(); + } + }, AuthenticationUtil.getSystemUserName()); + } + + // Write the JSON content to search node + final NodeRef writableSearchNode = searchNode; + AuthenticationUtil.runAs(new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + ContentWriter writer = fileFolderService.getWriter(writableSearchNode); + writer.setEncoding("UTF-8"); + writer.setMimetype(MimetypeMap.MIMETYPE_JSON); + writer.putContent(savedSearchDetails.toJSONString()); + + return null; + } + }, AuthenticationUtil.getSystemUserName()); + + return savedSearchDetails; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchService#deleteSavedSearch(java.lang.String, java.lang.String) + */ + @Override + public void deleteSavedSearch(String siteId, String name) + { + // Check parameters + ParameterCheck.mandatory("siteId", siteId); + ParameterCheck.mandatory("name", name); + + // Get the search node for the saved query + NodeRef searchNode = getSearchNodeRef(siteId, name); + if (searchNode != null && fileFolderService.exists(searchNode) == true) + { + fileFolderService.delete(searchNode); + } + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchService#deleteSavedSearch(org.alfresco.module.org_alfresco_module_rm.search.SavedSearchDetails) + */ + @Override + public void deleteSavedSearch(SavedSearchDetails savedSearchDetails) + { + // Check parameters + ParameterCheck.mandatory("savedSearchDetails", savedSearchDetails); + + // Delete the saved search + deleteSavedSearch(savedSearchDetails.getSiteId(), savedSearchDetails.getName()); + } + + /** + * Get the saved search node reference. + * @param siteId site id + * @param name search name + * @return {@link NodeRef} search node reference + */ + private NodeRef getSearchNodeRef(String siteId, String name) + { + NodeRef searchNode = null; + + // Get the root saved search container + NodeRef container = siteService.getContainer(siteId, SEARCH_CONTAINER); + if (container != null) + { + // try and find the search node + searchNode = fileFolderService.searchSimple(container, name); + + // can't find it so check the users container + if (searchNode == null) + { + String userName = AuthenticationUtil.getFullyAuthenticatedUser(); + NodeRef userContainer = fileFolderService.searchSimple(container, userName); + if (userContainer != null) + { + searchNode = fileFolderService.searchSimple(userContainer, name); + } + } + } + + return searchNode; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchService#addReports(java.lang.String) + */ + @Override + public void addReports(String siteId) + { + for (ReportDetails report : reports) + { + // Create saved search details + SavedSearchDetails savedSearchDetails = new SavedSearchDetails( + siteId, + report.getName(), + report.getDescription(), + report.getSearch(), + report.getSearchParameters(), + true, + true, + namespaceService, + this); + + // Save search details + saveSearch(savedSearchDetails); + } + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/search/ReportDetails.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/search/ReportDetails.java new file mode 100644 index 0000000000..4275a01907 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/search/ReportDetails.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.search; + + +/** + * Report details. + * + * @author Roy Wetherall + */ +public class ReportDetails +{ + /** Name */ + protected String name; + + /** Description */ + protected String description; + + /** Search */ + protected String search; + + /** Search parameters */ + protected RecordsManagementSearchParameters searchParameters; + + /** + * + * @param name + * @param description + * @param search + * @param searchParameters + */ + public ReportDetails(String name, String description, String search, RecordsManagementSearchParameters searchParameters) + { + this.name = name; + this.description = description; + this.search = search; + this.searchParameters = searchParameters; + } + + /** + * @return {@link String} name + */ + public String getName() + { + return name; + } + + /** + * @return {@link String} description + */ + public String getDescription() + { + return description; + } + + /** + * @param description description + */ + public void setDescription(String description) + { + this.description = description; + } + + /** + * @return {@link String} search string + */ + public String getSearch() + { + return search; + } + + /** + * @param query query string + */ + public void setSearch(String search) + { + this.search = search; + } + + /** + * @return + */ + public RecordsManagementSearchParameters getSearchParameters() + { + return searchParameters; + } + + /** + * @param searchParameters + */ + public void setSearchParameters(RecordsManagementSearchParameters searchParameters) + { + this.searchParameters = searchParameters; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/search/SavedSearchDetails.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/search/SavedSearchDetails.java new file mode 100644 index 0000000000..4c0dc935a1 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/search/SavedSearchDetails.java @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.search; + + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.util.ParameterCheck; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Saved search details. + * + * Example format of posted Saved Search JSON: + * + * { + * "siteid" : "rm", + * "name": "search name", + * "description": "the search description", + * "search": "the search sting as entered by the user", + * "public": boolean, + * "searchparams" : + * { + * "maxItems" : 500, + * "records" : true, + * "undeclaredrecords" : false, + * "vitalrecords" : false, + * "recordfolders" : false, + * "frozen" : false, + * "cutoff" : false, + * "containertypes" : + * [ + * "rma:recordSeries", + * "rma:recordCategory" + * ] + * "sort" : + * [ + * { + * "field" : "cm:name", + * "ascending" : true + * } + * ] + * } + * } + * + * where: name and query values are mandatory, + * searchparams contains the filters, sort, etc information about the query + * query is there for backward compatibility + * note: + * "params": "terms=keywords:xyz&undeclared=true", + * "sort": "cm:name/asc" + * "query": "the complete search query string", + * ... are sometimes found in the place of searchparams and are migrated to the new format when re-saved + * params are in URL encoded name/value pair format + * sort is in comma separated "property/dir" packed format i.e. "cm:name/asc,cm:title/desc" + * + * @author Roy Wetherall + */ +public class SavedSearchDetails extends ReportDetails +{ + // JSON label values + public static final String SITE_ID = "siteid"; + public static final String NAME = "name"; + public static final String DESCRIPTION = "description"; + public static final String SEARCH = "search"; + public static final String PUBLIC = "public"; + public static final String REPORT = "report"; + public static final String SEARCHPARAMS = "searchparams"; + + // JSON values for backwards compatibility + public static final String QUERY = "query"; + public static final String SORT = "sort"; + public static final String PARAMS = "params"; + + /** Site id */ + private String siteId; + + /** Indicates whether the saved search is public or not */ + private boolean isPublic; + + /** Indicates whether the saved search is a report */ + private boolean isReport = false; + + /** Namespace service */ + NamespaceService namespaceService; + + RecordsManagementSearchServiceImpl searchService; + + private SavedSearchDetailsCompatibility compatibility; + + /** + * + * @param jsonString + * @return + */ + /*package*/ static SavedSearchDetails createFromJSON(String jsonString, NamespaceService namespaceService, RecordsManagementSearchServiceImpl searchService) + { + try + { + JSONObject search = new JSONObject(jsonString); + + // Get the site id + if (search.has(SITE_ID) == false) + { + throw new AlfrescoRuntimeException("Can not create saved search details from json, because required siteid is not present. " + jsonString); + } + String siteId = search.getString(SITE_ID); + + // Get the name + if (search.has(NAME) == false) + { + throw new AlfrescoRuntimeException("Can not create saved search details from json, because required name is not present. " + jsonString); + } + String name = search.getString(NAME); + + // Get the description + String description = ""; + if (search.has(DESCRIPTION) == true) + { + description = search.getString(DESCRIPTION); + } + + // Get the query + String query = null; + if (search.has(SEARCH) == false) + { + // We are probably dealing with a "old" style saved search + if (search.has(PARAMS) == true) + { + String oldParams = search.getString(PARAMS); + query = SavedSearchDetailsCompatibility.getSearchFromParams(oldParams); + } + else + { + throw new AlfrescoRuntimeException("Can not create saved search details from json, because required search is not present. " + jsonString); + } + + } + else + { + query = search.getString(SEARCH); + } + + // Get the search parameters + RecordsManagementSearchParameters searchParameters = new RecordsManagementSearchParameters(); + if (search.has(SEARCHPARAMS) == true) + { + searchParameters = RecordsManagementSearchParameters.createFromJSON(search.getJSONObject(SEARCHPARAMS), namespaceService); + } + else + { + // See if we are dealing with the old style of saved search + if (search.has(PARAMS) == true) + { + String oldParams = search.getString(PARAMS); + String oldSort = search.getString(SORT); + searchParameters = SavedSearchDetailsCompatibility.createSearchParameters(oldParams, oldSort, namespaceService); + } + } + + // Determine whether the saved query is public or not + boolean isPublic = false; + if (search.has(PUBLIC) == true) + { + isPublic = search.getBoolean(PUBLIC); + } + + // Determine whether the saved query is a report or not + boolean isReport = false; + if (search.has(REPORT) == true) + { + isReport = search.getBoolean(REPORT); + } + + // Create the saved search details object + return new SavedSearchDetails(siteId, name, description, query, searchParameters, isPublic, isReport, namespaceService, searchService); + } + catch (JSONException exception) + { + throw new AlfrescoRuntimeException("Can not create saved search details from json. " + jsonString, exception); + } + } + + /** + * @param siteId + * @param name + * @param description + * @param isPublic + */ + /*package*/ SavedSearchDetails( + String siteId, + String name, + String description, + String serach, + RecordsManagementSearchParameters searchParameters, + boolean isPublic, + boolean isReport, + NamespaceService namespaceService, + RecordsManagementSearchServiceImpl searchService) + { + super(name, description, serach, searchParameters); + + ParameterCheck.mandatory("siteId", siteId); + ParameterCheck.mandatory("namespaceService", namespaceService); + ParameterCheck.mandatory("searchService", searchService); + + this.siteId = siteId; + this.isPublic = isPublic; + this.isReport = isReport; + this.namespaceService = namespaceService; + this.compatibility = new SavedSearchDetailsCompatibility(this, namespaceService, searchService); + this.searchService = searchService; + } + + /** + * @return + */ + public String getSiteId() + { + return siteId; + } + + /** + * @return + */ + public boolean isPublic() + { + return isPublic; + } + + /** + * @return + */ + public boolean isReport() + { + return isReport; + } + + public SavedSearchDetailsCompatibility getCompatibility() + { + return compatibility; + } + + /** + * @return + */ + /*package*/ String toJSONString() + { + try + { + JSONObject jsonObject = new JSONObject(); + jsonObject.put(SITE_ID, siteId); + jsonObject.put(NAME, name); + jsonObject.put(DESCRIPTION, description); + jsonObject.put(SEARCH, search); + jsonObject.put(SEARCHPARAMS, searchParameters.toJSONObject(namespaceService)); + jsonObject.put(PUBLIC, isPublic); + + // Add full query for backward compatibility + jsonObject.put(QUERY, searchService.buildQueryString(search, searchParameters)); + jsonObject.put(SORT, compatibility.getSort()); + + return jsonObject.toString(); + } + catch (JSONException exception) + { + throw new AlfrescoRuntimeException("Can not convert saved search details into JSON.", exception); + } + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/search/SavedSearchDetailsCompatibility.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/search/SavedSearchDetailsCompatibility.java new file mode 100644 index 0000000000..c4882ec968 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/search/SavedSearchDetailsCompatibility.java @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.search; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * Compatibility class. + * + * Used to bridge between the old style of saved search passed and required by the UI and the new actual saved search details. + * Eventually will be factored out as web scripts are brought up to date. + */ +public class SavedSearchDetailsCompatibility implements RecordsManagementModel +{ + /** Saved search details */ + private final SavedSearchDetails savedSearchDetails; + + /** Namespace service */ + private final NamespaceService namespaceService; + + /** Records management search service implementation */ + private final RecordsManagementSearchServiceImpl searchService; + + /** + * Retrieve the search from the parameter string + * @param params parameter string + * @return String search term + */ + public static String getSearchFromParams(String params) + { + String search = null; + String[] values = params.split("&"); + for (String value : values) + { + if (value.startsWith("terms") == true) + { + String[] terms = value.trim().split("="); + try + { + search = URLDecoder.decode(terms[1], "UTF-8"); + } + catch (UnsupportedEncodingException e) + { + // Do nothing just return null + search = null; + } + break; + } + } + + return search; + } + + public static RecordsManagementSearchParameters createSearchParameters(String params, String sort, NamespaceService namespaceService) + { + return createSearchParameters(params, new String[]{"&", "="}, sort, namespaceService); + } + + /** + * + * @param params + * @param sort + * @param namespaceService + * @return + */ + public static RecordsManagementSearchParameters createSearchParameters(String params, String[] paramsDelim, String sort, NamespaceService namespaceService) + { + RecordsManagementSearchParameters result = new RecordsManagementSearchParameters(); + List includedContainerTypes = new ArrayList(2); + + // Map the param values into the search parameter object + String[] values = params.split(paramsDelim[0]); + for (String value : values) + { + String[] paramValues = value.split(paramsDelim[1]); + String paramName = paramValues[0].trim(); + String paramValue = paramValues[1].trim(); + if ("records".equals(paramName) == true) + { + result.setIncludeRecords(Boolean.parseBoolean(paramValue)); + } + else if ("undeclared".equals(paramName) == true) + { + result.setIncludeUndeclaredRecords(Boolean.parseBoolean(paramValue)); + } + else if ("vital".equals(paramName) == true) + { + result.setIncludeVitalRecords(Boolean.parseBoolean(paramValue)); + } + else if ("folders".equals(paramName) == true) + { + result.setIncludeRecordFolders(Boolean.parseBoolean(paramValue)); + } + else if ("frozen".equals(paramName) == true) + { + result.setIncludeFrozen(Boolean.parseBoolean(paramValue)); + } + else if ("cutoff".equals(paramName) == true) + { + result.setIncludeCutoff(Boolean.parseBoolean(paramValue)); + } + else if ("categories".equals(paramName) == true && Boolean.parseBoolean(paramValue) == true) + { + includedContainerTypes.add(TYPE_RECORD_CATEGORY); + } +// else if ("series".equals(paramName) == true && Boolean.parseBoolean(paramValue) == true) +// { +// includedContainerTypes.add(DOD5015Model.TYPE_RECORD_SERIES); +// } + } + result.setIncludedContainerTypes(includedContainerTypes); + + if (sort != null) + { + // Map the sort string into the search details + String[] sortPairs = sort.split(","); + Map sortOrder = new HashMap(sortPairs.length); + for (String sortPairString : sortPairs) + { + String[] sortPair = sortPairString.split("/"); + QName field = QName.createQName(sortPair[0], namespaceService); + Boolean isAcsending = Boolean.FALSE; + if ("asc".equals(sortPair[1]) == true) + { + isAcsending = Boolean.TRUE; + } + sortOrder.put(field, isAcsending); + } + result.setSortOrder(sortOrder); + } + + return result; + } + + /** + * Constructor + * @param savedSearchDetails + */ + public SavedSearchDetailsCompatibility(SavedSearchDetails savedSearchDetails, + NamespaceService namespaceService, + RecordsManagementSearchServiceImpl searchService) + { + this.savedSearchDetails = savedSearchDetails; + this.namespaceService = namespaceService; + this.searchService = searchService; + } + + /** + * Get the sort string from the saved search details + * @return + */ + public String getSort() + { + StringBuilder builder = new StringBuilder(64); + + for (Map.Entry entry : this.savedSearchDetails.getSearchParameters().getSortOrder().entrySet()) + { + if (builder.length() !=0) + { + builder.append(","); + } + + String order = "desc"; + if (Boolean.TRUE.equals(entry.getValue()) == true) + { + order = "asc"; + } + builder.append(entry.getKey().toPrefixString(this.namespaceService)) + .append("/") + .append(order); + } + + return builder.toString(); + } + + /** + * Get the parameter string from the saved search details + * @return + */ + public String getParams() + { + List includeContainerTypes = this.savedSearchDetails.getSearchParameters().getIncludedContainerTypes(); + StringBuilder builder = new StringBuilder(128); + builder.append("terms=").append(this.savedSearchDetails.getSearch()).append("&") + .append("records=").append(this.savedSearchDetails.getSearchParameters().isIncludeRecords()).append("&") + .append("undeclared=").append(this.savedSearchDetails.getSearchParameters().isIncludeUndeclaredRecords()).append("&") + .append("vital=").append(this.savedSearchDetails.getSearchParameters().isIncludeVitalRecords()).append("&") + .append("folders=").append(this.savedSearchDetails.getSearchParameters().isIncludeRecordFolders()).append("&") + .append("frozen=").append(this.savedSearchDetails.getSearchParameters().isIncludeFrozen()).append("&") + .append("cutoff=").append(this.savedSearchDetails.getSearchParameters().isIncludeCutoff()).append("&") + .append("categories=").append(includeContainerTypes.contains(TYPE_RECORD_CATEGORY)).append("&") + .append("series=").append(false); + return builder.toString(); + } + + /** + * Build the full query string + * @return + */ + public String getQuery() + { + return searchService.buildQueryString(this.savedSearchDetails.getSearch(), this.savedSearchDetails.getSearchParameters()); + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/RecordsManagementSecurityService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/RecordsManagementSecurityService.java new file mode 100644 index 0000000000..781844762b --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/RecordsManagementSecurityService.java @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.security; + +import java.util.Map; +import java.util.Set; + +import org.alfresco.module.org_alfresco_module_rm.capability.Capability; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.namespace.QName; + +/** + * Records management permission service interface + * + * @author Roy Wetherall + */ +public interface RecordsManagementSecurityService +{ + /** + * Get a list of the capabilities available + * + * @return List list of capabilities available + */ + Set getCapabilities(); + + /** + * Get the full set of capabilities for the current user. + * @param nodeRef + * @return + */ + Map getCapabilities(NodeRef nodeRef); + + /** + * + * @param nodeRef + * @param capabilitySet + * @return + */ + Map getCapabilities(NodeRef nodeRef, String capabilitySet); + + /** + * Get a capability by name + * @param name + * @return + */ + Capability getCapability(String name); + + /** + * Get the set of aspect QNames which can not be added direct via the public node service; + * they must be managed via the appropriate actions. + * @return + */ + Set getProtectedAspects(); + + /** + * Get the set of property QNames which can not be added, updated or removed direct via the public node service; + * they must be managed via the appropriate actions. + * @return + */ + Set getProtectedProperties(); + + /** + * Creates the initial set of default roles for a root records management node + * + * @param rmRootNode + */ + void bootstrapDefaultRoles(NodeRef rmRootNode); + + /** + * Get all the available roles for the given records management root node + * + * @param rmRootNode + * @return + */ + Set getRoles(NodeRef rmRootNode); + + /** + * Gets the roles for a given user + * + * @param rmRootNode + * @param user + * @return + */ + Set getRolesByUser(NodeRef rmRootNode, String user); + + /** + * Get a role by name + * + * @param rmRootNode + * @param role + * @return + */ + Role getRole(NodeRef rmRootNode, String role); + + /** + * Indicate whether a role exists for a given records management root node + * @param rmRootNode + * @param role + * @return + */ + boolean existsRole(NodeRef rmRootNode, String role); + + /** + * Determines whether the given user has the RM Admin role + * + * @param rmRootNode RM root node + * @param user user name to check + * @return true if the user has the RM Admin role, false otherwise + */ + boolean hasRMAdminRole(NodeRef rmRootNode, String user); + + /** + * Create a new role + * + * @param rmRootNode + * @param role + * @param roleDisplayLabel + * @param capabilities + * @return + */ + Role createRole(NodeRef rmRootNode, String role, String roleDisplayLabel, Set capabilities); + + /** + * Update an existing role + * + * @param rmRootNode + * @param role + * @param roleDisplayLabel + * @param capabilities + * @return + */ + Role updateRole(NodeRef rmRootNode, String role, String roleDisplayLabel, Set capabilities); + + /** + * Delete a role + * + * @param rmRootNode + * @param role + */ + void deleteRole(NodeRef rmRootNode, String role); + + /** + * Assign a role to an authority + * + * @param authorityName + * @param rmRootNode + * @param role + */ + void assignRoleToAuthority(NodeRef rmRootNode, String role, String authorityName); + + /** + * Sets a permission on a RM object. Assumes allow is true. Cascades permission down to record folder. + * Cascades ReadRecord up to file plan. + * + * @param nodeRef node reference + * @param authority authority + * @param permission permission + */ + void setPermission(NodeRef nodeRef, String authority, String permission); + + /** + * Deletes a permission from a RM object. Cascades removal down to record folder. + * + * @param nodeRef node reference + * @param authority authority + * @param permission permission + */ + void deletePermission(NodeRef nodeRef, String authority, String permission); +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/RecordsManagementSecurityServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/RecordsManagementSecurityServiceImpl.java new file mode 100644 index 0000000000..513bac8102 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/RecordsManagementSecurityServiceImpl.java @@ -0,0 +1,975 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.security; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.capability.Capability; +import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService; +import org.alfresco.module.org_alfresco_module_rm.capability.RMEntryVoter; +import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.policy.Behaviour.NotificationFrequency; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.permissions.AccessDeniedException; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.security.AccessPermission; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.service.cmr.security.OwnableService; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.util.ParameterCheck; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Records management permission service implementation + * + * @author Roy Wetherall + */ +public class RecordsManagementSecurityServiceImpl implements RecordsManagementSecurityService, + RecordsManagementModel + +{ + /** Capability service */ + private CapabilityService capabilityService; + + /** Authority service */ + private AuthorityService authorityService; + + /** Permission service */ + private PermissionService permissionService; + + /** Policy component */ + private PolicyComponent policyComponent; + + /** Owner service */ + private OwnableService ownableService; + + /** Records management service */ + private RecordsManagementService recordsManagementService; + + /** Node service */ + private NodeService nodeService; + + /** RM Entry voter */ + private RMEntryVoter voter; + + /** + * Capability sets. Allow sub-sets of capabilities to be defined enhancing performance when + * only a sub-set need be evaluated. + */ + private Map> capabilitySets; + + /** Records management role zone */ + public static final String RM_ROLE_ZONE_PREFIX = "rmRoleZone"; + + /** Logger */ + private static Log logger = LogFactory.getLog(RecordsManagementSecurityServiceImpl.class); + + /** + * Set the capability service + * + * @param capabilityService + */ + public void setCapabilityService(CapabilityService capabilityService) + { + this.capabilityService = capabilityService; + } + + /** + * Set the authortiy service + * + * @param authorityService + */ + public void setAuthorityService(AuthorityService authorityService) + { + this.authorityService = authorityService; + } + + /** + * Set the permission service + * + * @param permissionService + */ + public void setPermissionService(PermissionService permissionService) + { + this.permissionService = permissionService; + } + + /** + * Set the policy component + * + * @param policyComponent + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * Set the ownable service + * + * @param ownableService ownable service + */ + public void setOwnableService(OwnableService ownableService) + { + this.ownableService = ownableService; + } + + /** + * Set records management service + * + * @param recordsManagementService records management service + */ + public void setRecordsManagementService(RecordsManagementService recordsManagementService) + { + this.recordsManagementService = recordsManagementService; + } + + /** + * Set the node service + * + * @param nodeService + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Set the capability sets + * @param capabilitySets map of capability sets (configured in Spring) + */ + public void setCapabilitySets(Map> capabilitySets) + { + this.capabilitySets = capabilitySets; + } + + /** + * Set the RM voter + * + * @param voter + */ + public void setVoter(RMEntryVoter voter) + { + this.voter = voter; + } + + /** + * Initialisation method + */ + public void init() + { + policyComponent.bindClassBehaviour(NodeServicePolicies.OnCreateNodePolicy.QNAME, + TYPE_FILE_PLAN, + new JavaBehaviour(this, "onCreateRootNode", NotificationFrequency.TRANSACTION_COMMIT)); + policyComponent.bindClassBehaviour(NodeServicePolicies.OnCreateNodePolicy.QNAME, + TYPE_RECORD_CATEGORY, + new JavaBehaviour(this, "onCreateRMContainer", NotificationFrequency.TRANSACTION_COMMIT)); + policyComponent.bindClassBehaviour(NodeServicePolicies.OnCreateNodePolicy.QNAME, + TYPE_RECORD_FOLDER, + new JavaBehaviour(this, "onCreateRecordFolder", NotificationFrequency.TRANSACTION_COMMIT)); + policyComponent.bindClassBehaviour(NodeServicePolicies.BeforeDeleteNodePolicy.QNAME, + ASPECT_FROZEN, + new JavaBehaviour(this, "beforeDeleteFrozenNode", NotificationFrequency.TRANSACTION_COMMIT)); + } + + public void beforeDeleteFrozenNode(NodeRef nodeRef) + { + throw new AccessDeniedException("Frozen nodes can not be deleted"); + } + + /** + * Create root node behaviour + * + * @param childAssocRef + */ + public void onCreateRootNode(ChildAssociationRef childAssocRef) + { + final NodeRef rmRootNode = childAssocRef.getChildRef(); + + // Do not execute behaviour if this has been created in the archive store + if(rmRootNode.getStoreRef().equals(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE) == true) + { + // This is not the spaces store - probably the archive store + return; + } + + if (nodeService.exists(rmRootNode) == true) + { + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Object doWork() + { + // Create "all" role group for root node + String allRoles = authorityService.createAuthority(AuthorityType.GROUP, getAllRolesGroupShortName(rmRootNode), "All Roles", null); + + // Set the permissions + permissionService.setInheritParentPermissions(rmRootNode, false); + permissionService.setPermission(rmRootNode, allRoles, RMPermissionModel.READ_RECORDS, true); + return null; + } + }, AuthenticationUtil.getAdminUserName()); + + // Bootstrap in the default set of roles for the newly created root node + bootstrapDefaultRoles(rmRootNode); + } + } + + /** + * Delete root node behaviour + * + * @param childAssocRef + */ + public void onDeleteRootNode(NodeRef rmRootNode) + { + logger.debug("onDeleteRootNode called"); + } + + /** + * Get all the roles by short name + * + * @param rmRootNode + * @return + */ + private String getAllRolesGroupShortName(NodeRef rmRootNode) + { + return "AllRoles" + rmRootNode.getId(); + } + + /** + * @param childAssocRef + */ + public void onCreateRMContainer(ChildAssociationRef childAssocRef) + { + setUpPermissions(childAssocRef.getChildRef()); + } + + /** + * @param childAssocRef + */ + public void onCreateRecordFolder(ChildAssociationRef childAssocRef) + { + final NodeRef folderNodeRef = childAssocRef.getChildRef(); + setUpPermissions(folderNodeRef); + + // Pull any permissions found on the parent (ie the record category) + final NodeRef catNodeRef = childAssocRef.getParentRef(); + if (nodeService.exists(catNodeRef) == true) + { + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Object doWork() + { + Set perms = permissionService.getAllSetPermissions(catNodeRef); + for (AccessPermission perm : perms) + { + AccessStatus accessStatus = perm.getAccessStatus(); + boolean allow = false; + if (AccessStatus.ALLOWED.equals(accessStatus) == true) + { + allow = true; + } + permissionService.setPermission( + folderNodeRef, + perm.getAuthority(), + perm.getPermission(), + allow); + } + + return null; + } + }, AuthenticationUtil.getAdminUserName()); + } + } + + /** + * + * @param nodeRef + */ + public void setUpPermissions(final NodeRef nodeRef) + { + if (nodeService.exists(nodeRef) == true) + { + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Object doWork() + { + // Break inheritance + permissionService.setInheritParentPermissions(nodeRef, false); + + return null; + } + }, AuthenticationUtil.getAdminUserName()); + } + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#getCapabilities() + */ + public Set getCapabilities() + { + Collection caps = capabilityService.getCapabilities(); + Set result = new HashSet(caps.size()); + for (Capability cap : caps) + { + if (cap.isPrivate() == false) + { + result.add(cap); + } + } + return result; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#getCapabilities(org.alfresco.service.cmr.repository.NodeRef) + */ + public Map getCapabilities(NodeRef nodeRef) + { + return capabilityService.getCapabilitiesAccessState(nodeRef); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#getCapabilities(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public Map getCapabilities(NodeRef nodeRef, String capabilitySet) + { + List capabilities = capabilitySets.get(capabilitySet); + if (capabilities == null) + { + if (getCapability(capabilitySet) != null) + { + // If the capability set is the name of a capability assume we just want that single + // capability + capabilities = new ArrayList(1); + capabilities.add(capabilitySet); + } + else + { + throw new AlfrescoRuntimeException("Unable to find the capability set '" + capabilitySet + "'"); + } + } + + return capabilityService.getCapabilitiesAccessState(nodeRef, capabilities); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#getCapability(java.lang.String) + */ + public Capability getCapability(String name) + { + return capabilityService.getCapability(name); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#getProtectedAspects() + */ + public Set getProtectedAspects() + { + return voter.getProtetcedAscpects(); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#getProtectedProperties() + */ + public Set getProtectedProperties() + { + return voter.getProtectedProperties(); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#bootstrapDefaultRoles(org.alfresco.service.cmr.repository.NodeRef) + */ + public void bootstrapDefaultRoles(final NodeRef rmRootNode) + { + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Object doWork() + { + try + { + JSONArray array = null; + try + { + // Load up the default roles from JSON + InputStream is = getClass().getClassLoader().getResourceAsStream("alfresco/module/org_alfresco_module_rm/security/rm-default-roles-bootstrap.json"); + if (is == null) + { + throw new AlfrescoRuntimeException("Could not load default bootstrap roles configuration"); + } + array = new JSONArray(convertStreamToString(is)); + } + catch (IOException ioe) + { + throw new AlfrescoRuntimeException("Unable to load rm-default-roles-bootstrap.json configuration file.", ioe); + } + + // Add each role to the rm root node + for (int i = 0; i < array.length(); i++) + { + JSONObject object = array.getJSONObject(i); + + // Get the name of the role + String name = null; + if (object.has("name") == true) + { + name = object.getString("name"); + if (existsRole(rmRootNode, name) == true) + { + throw new AlfrescoRuntimeException("The bootstrap role " + name + " already exists on the rm root node " + rmRootNode.toString()); + } + } + else + { + throw new AlfrescoRuntimeException("No name given to default bootstrap role. Check json configuration file."); + } + + + // Get the role's display label + String displayLabel = name; + if (object.has("displayLabel") == true) + { + displayLabel = object.getString("displayLabel"); + } + + // Determine whether the role is an admin role or not + boolean isAdmin = false; + if (object.has("isAdmin") == true) + { + isAdmin = object.getBoolean("isAdmin"); + } + + // Get the roles capabilities + Set capabilities = new HashSet(30); + if (object.has("capabilities") == true) + { + JSONArray arrCaps = object.getJSONArray("capabilities"); + for (int index = 0; index < arrCaps.length(); index++) + { + String capName = arrCaps.getString(index); + Capability capability = getCapability(capName); + if (capability == null) + { + throw new AlfrescoRuntimeException("The capability '" + capName + "' configured for the deafult boostrap role '" + name + "' is invalid."); + } + capabilities.add(capability); + } + } + + // Create the role + Role role = createRole(rmRootNode, name, displayLabel, capabilities); + + // Add any additional admin permissions + if (isAdmin == true) + { + permissionService.setPermission(rmRootNode, role.getRoleGroupName(), RMPermissionModel.FILING, true); + + // Add the owner of the root node into the admin group + //authorityService.addAuthority(role.getRoleGroupName(), ownableService.getOwner(rmRootNode)); + } + } + } + catch (JSONException exception) + { + throw new AlfrescoRuntimeException("Error loading json configuration file rm-default-roles-bootstrap.json", exception); + } + + return null; + } + }, AuthenticationUtil.getAdminUserName()); + } + + public String convertStreamToString(InputStream is) throws IOException + { + /* + * To convert the InputStream to String we use the BufferedReader.readLine() + * method. We iterate until the BufferedReader return null which means + * there's no more data to read. Each line will appended to a StringBuilder + * and returned as String. + */ + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + StringBuilder sb = new StringBuilder(); + + String line = null; + try + { + while ((line = reader.readLine()) != null) + { + sb.append(line + "\n"); + } + } + finally + { + try {is.close();} catch (IOException e) {} + } + + return sb.toString(); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#getRoles() + */ + public Set getRoles(final NodeRef rmRootNode) + { + return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork>() + { + public Set doWork() throws Exception + { + Set result = new HashSet(13); + + Set roleAuthorities = authorityService.getAllAuthoritiesInZone(getZoneName(rmRootNode), AuthorityType.GROUP); + for (String roleAuthority : roleAuthorities) + { + String name = getShortRoleName(authorityService.getShortName(roleAuthority), rmRootNode); + String displayLabel = authorityService.getAuthorityDisplayName(roleAuthority); + Set capabilities = getCapabilitiesImpl(rmRootNode, roleAuthority); + + Role role = new Role(name, displayLabel, capabilities, roleAuthority); + result.add(role); + } + + return result; + } + }, AuthenticationUtil.getAdminUserName()); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#getRolesByUser(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public Set getRolesByUser(final NodeRef rmRootNode, final String user) + { + return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork>() + { + public Set doWork() throws Exception + { + Set result = new HashSet(13); + + Set roleAuthorities = authorityService.getAllAuthoritiesInZone(getZoneName(rmRootNode), AuthorityType.GROUP); + for (String roleAuthority : roleAuthorities) + { + Set users = authorityService.getContainedAuthorities(AuthorityType.USER, roleAuthority, false); + if (users.contains(user) == true) + { + String name = getShortRoleName(authorityService.getShortName(roleAuthority), rmRootNode); + String displayLabel = authorityService.getAuthorityDisplayName(roleAuthority); + Set capabilities = getCapabilitiesImpl(rmRootNode, roleAuthority); + + Role role = new Role(name, displayLabel, capabilities, roleAuthority); + result.add(role); + } + } + + return result; + } + }, AuthenticationUtil.getAdminUserName()); + } + + /** + * + * @param rmRootNode + * @return + */ + private String getZoneName(NodeRef rmRootNode) + { + return RM_ROLE_ZONE_PREFIX + rmRootNode.getId(); + } + + /** + * Get the full role name + * + * @param role + * @param rmRootNode + * @return + */ + private String getFullRoleName(String role, NodeRef rmRootNode) + { + return role + rmRootNode.getId(); + } + + /** + * Get the short role name + * + * @param fullRoleName + * @param rmRootNode + * @return + */ + private String getShortRoleName(String fullRoleName, NodeRef rmRootNode) + { + return fullRoleName.replaceAll(rmRootNode.getId(), ""); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#getRole(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public Role getRole(final NodeRef rmRootNode, final String role) + { + return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Role doWork() throws Exception + { + Role result = null; + + String roleAuthority = authorityService.getName(AuthorityType.GROUP, getFullRoleName(role, rmRootNode)); + if (authorityService.authorityExists(roleAuthority) == true) + { + String name = getShortRoleName(authorityService.getShortName(roleAuthority), rmRootNode); + String displayLabel = authorityService.getAuthorityDisplayName(roleAuthority); + Set capabilities = getCapabilitiesImpl(rmRootNode, roleAuthority); + + result = new Role(name, displayLabel, capabilities, roleAuthority); + } + + return result; + } + }, AuthenticationUtil.getAdminUserName()); + } + + private Set getCapabilitiesImpl(NodeRef rmRootNode, String roleAuthority) + { + Set permissions = permissionService.getAllSetPermissions(rmRootNode); + Set capabilities = new HashSet(52); + for (AccessPermission permission : permissions) + + { + if (permission.getAuthority().equals(roleAuthority) == true) + { + String capabilityName = permission.getPermission(); + if (getCapability(capabilityName) != null) + { + capabilities.add(permission.getPermission()); + } + } + + } + + return capabilities; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#existsRole(java.lang.String) + */ + public boolean existsRole(final NodeRef rmRootNode, final String role) + { + return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Boolean doWork() throws Exception + { + String fullRoleName = authorityService.getName(AuthorityType.GROUP, getFullRoleName(role, rmRootNode)); + + String zone = getZoneName(rmRootNode); + Set roles = authorityService.getAllAuthoritiesInZone(zone, AuthorityType.GROUP); + return new Boolean(roles.contains(fullRoleName)); + } + }, AuthenticationUtil.getAdminUserName()).booleanValue(); + } + + /* + * @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#hasRMAdminRole(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public boolean hasRMAdminRole(NodeRef rmRootNode, String user) + { + boolean isRMAdmin = false; + + Set userRoles = this.getRolesByUser(rmRootNode, user); + if (userRoles != null) + { + for (Role role : userRoles) + { + if (role.getName().equals("Administrator")) + { + isRMAdmin = true; + break; + } + } + } + + return isRMAdmin; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#createRole(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, java.lang.String, java.util.Set) + */ + public Role createRole(final NodeRef rmRootNode, final String role, final String roleDisplayLabel, final Set capabilities) + { + return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Role doWork() throws Exception + { + String fullRoleName = getFullRoleName(role, rmRootNode); + + // Check that the role does not already exist for the rm root node + if (authorityService.authorityExists(authorityService.getName(AuthorityType.GROUP, fullRoleName))) + { + throw new AlfrescoRuntimeException("The role " + role + " already exists for root rm node " + rmRootNode.getId()); + } + + // Create a group that relates to the records management role + Set zones = new HashSet(2); + zones.add(getZoneName(rmRootNode)); + zones.add(AuthorityService.ZONE_APP_DEFAULT); + String roleGroup = authorityService.createAuthority(AuthorityType.GROUP, fullRoleName, roleDisplayLabel, zones); + + // Add the roleGroup to the "all" role group + String allRoleGroup = authorityService.getName(AuthorityType.GROUP, getAllRolesGroupShortName(rmRootNode)); + authorityService.addAuthority(allRoleGroup, roleGroup); + + // Assign the various capabilities to the group on the root records management node + Set capStrings = new HashSet(53); + if (capabilities != null) + { + for (Capability capability : capabilities) + { + permissionService.setPermission(rmRootNode, roleGroup, capability.getName(), true); + } + + // Create the role + for (Capability capability : capabilities) + { + capStrings.add(capability.getName()); + } + } + + return new Role(role, roleDisplayLabel, capStrings, roleGroup); + } + }, AuthenticationUtil.getAdminUserName()); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#updateRole(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, java.lang.String, java.util.Set) + */ + public Role updateRole(final NodeRef rmRootNode, final String role, final String roleDisplayLabel, final Set capabilities) + { + return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Role doWork() throws Exception + { + String roleAuthority = authorityService.getName(AuthorityType.GROUP, getFullRoleName(role, rmRootNode)); + + // Reset the role display name + authorityService.setAuthorityDisplayName(roleAuthority, roleDisplayLabel); + + // TODO this needs to be improved, removing all and readding is not ideal + + // Clear the current capabilities + permissionService.clearPermission(rmRootNode, roleAuthority); + + // Re-add the provided capabilities + for (Capability capability : capabilities) + { + permissionService.setPermission(rmRootNode, roleAuthority, capability.getName(), true); + } + + Set capStrings = new HashSet(capabilities.size()); + for (Capability capability : capabilities) + { + capStrings.add(capability.getName()); + } + return new Role(role, roleDisplayLabel, capStrings, roleAuthority); + + } + }, AuthenticationUtil.getAdminUserName()); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#deleteRole(java.lang.String) + */ + public void deleteRole(final NodeRef rmRootNode, final String role) + { + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Boolean doWork() throws Exception + { + String roleAuthority = authorityService.getName(AuthorityType.GROUP, getFullRoleName(role, rmRootNode)); + authorityService.deleteAuthority(roleAuthority); + return null; + + } + }, AuthenticationUtil.getAdminUserName()); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#assignRoleToAuthority(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, java.lang.String) + */ + public void assignRoleToAuthority(final NodeRef rmRootNode, final String role, final String authorityName) + { + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Boolean doWork() throws Exception + { + String roleAuthority = authorityService.getName(AuthorityType.GROUP, getFullRoleName(role, rmRootNode)); + authorityService.addAuthority(roleAuthority, authorityName); + return null; + + } + }, AuthenticationUtil.getAdminUserName()); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#setPermission(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, java.lang.String, boolean) + */ + public void setPermission(final NodeRef nodeRef, final String authority, final String permission) + { + ParameterCheck.mandatory("nodeRef", nodeRef); + ParameterCheck.mandatory("authority", authority); + ParameterCheck.mandatory("permission", permission); + + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Boolean doWork() throws Exception + { + if (recordsManagementService.isFilePlan(nodeRef) == false && + recordsManagementService.isRecordCategory(nodeRef) == true) + { + setReadPermissionUp(nodeRef, authority); + setPermissionDown(nodeRef, authority, permission); + } + else if (recordsManagementService.isRecordFolder(nodeRef) == true) + { + setReadPermissionUp(nodeRef, authority); + setPermissionImpl(nodeRef, authority, permission); + } + else + { + if (logger.isWarnEnabled() == true) + { + logger.warn("Setting permissions for this node is not supported. (nodeRef=" + nodeRef + ", authority=" + authority + ", permission=" + permission + ")"); + } + } + + return null; + } + }, AuthenticationUtil.getAdminUserName()); + } + + /** + * Helper method to set the read permission up the hierarchy + * + * @param nodeRef + * @param authority + */ + private void setReadPermissionUp(NodeRef nodeRef, String authority) + { + NodeRef parent = nodeService.getPrimaryParent(nodeRef).getParentRef(); + if (parent != null && + recordsManagementService.isFilePlan(parent) == false) + { + setPermissionImpl(parent, authority, RMPermissionModel.READ_RECORDS); + setReadPermissionUp(parent, authority); + } + } + + /** + * Helper method to set the permission down the hierarchy + * + * @param nodeRef + * @param authority + * @param permission + */ + private void setPermissionDown(NodeRef nodeRef, String authority, String permission) + { + setPermissionImpl(nodeRef, authority, permission); + if (recordsManagementService.isRecordCategory(nodeRef) == true) + { + List assocs = nodeService.getChildAssocs(nodeRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef assoc : assocs) + { + NodeRef child = assoc.getChildRef(); + if (recordsManagementService.isRecordCategory(child) == true || + recordsManagementService.isRecordFolder(child) == true) + { + setPermissionDown(child, authority, permission); + } + } + } + } + + /** + * Set the permission, taking into account that filing is a superset of read + * + * @param nodeRef + * @param authority + * @param permission + */ + private void setPermissionImpl(NodeRef nodeRef, String authority, String permission) + { + if (RMPermissionModel.FILING.equals(permission) == true) + { + // Remove record read permission before adding filing permission + permissionService.deletePermission(nodeRef, authority, RMPermissionModel.READ_RECORDS); + } + + permissionService.setPermission(nodeRef, authority, permission, true); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#deletePermission(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, java.lang.String) + */ + public void deletePermission(final NodeRef nodeRef, final String authority, final String permission) + { + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Boolean doWork() throws Exception + { + // Delete permission on this node + permissionService.deletePermission(nodeRef, authority, permission); + + if (recordsManagementService.isRecordCategory(nodeRef) == true) + { + List assocs = nodeService.getChildAssocs(nodeRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef assoc : assocs) + { + NodeRef child = assoc.getChildRef(); + if (recordsManagementService.isRecordCategory(child) == true || + recordsManagementService.isRecordFolder(child) == true) + { + deletePermission(child, authority, permission); + } + } + } + + return null; + } + }, AuthenticationUtil.getAdminUserName()); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/Role.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/Role.java new file mode 100644 index 0000000000..79b4986979 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/Role.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.security; + +import java.util.Set; + +/** + * Records management role class + * + * @author Roy Wetherall + */ +public class Role +{ + private String name; + private String displayLabel; + private Set capabilities; + private String roleGroupName; + + /** + * @param name + * @param displayLabel + * @param capabilities + */ + public Role(String name, String displayLabel, Set capabilities, String roleGroupName) + { + this.name = name; + this.displayLabel = displayLabel; + this.capabilities = capabilities; + this.roleGroupName = roleGroupName; + } + + /** + * @return the name + */ + public String getName() + { + return name; + } + + /** + * @return the displayLabel + */ + public String getDisplayLabel() + { + return displayLabel; + } + + /** + * @return the capabilities + */ + public Set getCapabilities() + { + return capabilities; + } + + /** + * @return the roleGroupName + */ + public String getRoleGroupName() + { + return roleGroupName; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/vital/BroadcastVitalRecordDefinitionAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/vital/BroadcastVitalRecordDefinitionAction.java new file mode 100644 index 0000000000..e7adec62e7 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/vital/BroadcastVitalRecordDefinitionAction.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.vital; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.Period; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; + +/** + * Action to implement the consequences of a change to the value of the VitalRecordDefinition properties. When the + * VitalRecordIndicator or the reviewPeriod properties are changed on a record container, then any descendant folders or + * records must be updated as a consequence. Descendant folders should have their reviewPeriods and/or + * vitalRecordIndicators updated to match the new value. Descendant records should have their reviewAsOf date updated. + * + * @author Neil McErlean + */ +public class BroadcastVitalRecordDefinitionAction extends RMActionExecuterAbstractBase +{ + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, + * org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + this.propagateChangeToChildrenOf(actionedUponNodeRef); + } + + /** + * @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List) + */ + @Override + protected void addParameterDefinitions(List paramList) + { + // Intentionally empty + } + + private void propagateChangeToChildrenOf(NodeRef actionedUponNodeRef) + { + Map parentProps = nodeService.getProperties(actionedUponNodeRef); + boolean parentVri = (Boolean) parentProps.get(PROP_VITAL_RECORD_INDICATOR); + Period parentReviewPeriod = (Period) parentProps.get(PROP_REVIEW_PERIOD); + + List assocs = this.nodeService.getChildAssocs(actionedUponNodeRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef nextAssoc : assocs) + { + NodeRef nextChild = nextAssoc.getChildRef(); + + // If the child is a record, then the VitalRecord aspect needs to be applied or updated + if (recordsManagementService.isRecord(nextChild)) + { + if (parentVri) + { + VitalRecordDefinition vrDefn = vitalRecordService.getVitalRecordDefinition(nextChild); + Map aspectProps = new HashMap(); + aspectProps.put(PROP_REVIEW_AS_OF, vrDefn.getNextReviewDate()); + + nodeService.addAspect(nextChild, RecordsManagementModel.ASPECT_VITAL_RECORD, aspectProps); + } + else + { + nodeService.removeAspect(nextChild, RecordsManagementModel.ASPECT_VITAL_RECORD); + } + } + else + // copy the vitalRecordDefinition properties from the parent to the child + { + Map childProps = nodeService.getProperties(nextChild); + childProps.put(PROP_REVIEW_PERIOD, parentReviewPeriod); + childProps.put(PROP_VITAL_RECORD_INDICATOR, parentVri); + nodeService.setProperties(nextChild, childProps); + } + + // Recurse down the containment hierarchy to all containers + if (recordsManagementService.isRecord(nextChild) == false) + { + this.propagateChangeToChildrenOf(nextChild); + } + } + } + + @Override + public boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + return true; + } + + @Override + public Set getProtectedProperties() + { + HashSet qnames = new HashSet(); + qnames.add(PROP_REVIEW_PERIOD); + qnames.add(PROP_VITAL_RECORD_INDICATOR); + qnames.add(PROP_REVIEW_AS_OF); + return qnames; + } + + @Override + public Set getProtectedAspects() + { + HashSet qnames = new HashSet(); + qnames.add(RecordsManagementModel.ASPECT_VITAL_RECORD); + return qnames; + } + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/vital/ReviewedAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/vital/ReviewedAction.java new file mode 100644 index 0000000000..731d7b67fe --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/vital/ReviewedAction.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.vital; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Reviewed action. + * + * @author Neil McErlean + */ +public class ReviewedAction extends RMActionExecuterAbstractBase +{ + private static Log logger = LogFactory.getLog(ReviewedAction.class); + + /** + * + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, + * org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + VitalRecordDefinition vrDef = vitalRecordService.getVitalRecordDefinition(actionedUponNodeRef); + if (vrDef != null && vrDef.isEnabled() == true) + { + if (recordsManagementService.isRecord(actionedUponNodeRef) == true) + { + reviewRecord(actionedUponNodeRef, vrDef); + } + else if (recordsManagementService.isRecordFolder(actionedUponNodeRef) == true) + { + for (NodeRef record : recordsManagementService.getRecords(actionedUponNodeRef)) + { + reviewRecord(record, vrDef); + } + } + } + } + + /** + * Make record as reviewed. + * + * @param nodeRef + * @param vrDef + */ + private void reviewRecord(NodeRef nodeRef, VitalRecordDefinition vrDef) + { + // Calculate the next review date + Date reviewAsOf = vrDef.getNextReviewDate(); + if (reviewAsOf != null) + { + // Log + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Setting new reviewAsOf property [") + .append(reviewAsOf) + .append("] on ") + .append(nodeRef); + logger.debug(msg.toString()); + } + + this.nodeService.setProperty(nodeRef, PROP_REVIEW_AS_OF, reviewAsOf); + //TODO And record previous review date, time, user + } + } + + /** + * + * @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List) + */ + @Override + protected void addParameterDefinitions(List paramList) + { + // Intentionally empty + } + + @Override + public Set getProtectedProperties() + { + HashSet qnames = new HashSet(); + qnames.add(PROP_REVIEW_AS_OF); + return qnames; + } + + @Override + protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + return true; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/vital/VitalRecordDefinition.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/vital/VitalRecordDefinition.java new file mode 100644 index 0000000000..e83c79d9a8 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/vital/VitalRecordDefinition.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.vital; + +import java.util.Date; + +import org.alfresco.service.cmr.repository.Period; + +/** + * Vital record definition interface + * + * @author Roy Wetherall + */ +public interface VitalRecordDefinition +{ + /** + * Indicates whether the vital record definition is enabled or not. + *

+ * Note: a result of false indicates that the vital record definition is inactive + * therefore does not impose the rules associated with vital record review on + * associated nodes. + * + * @return boolean true if enabled, false otherwise + */ + boolean isEnabled(); + + /** + * Review period for vital records + * + * @return Period review period + */ + Period getReviewPeriod(); + + /** + * Gets the next review date based on the review period + * + * @return Date date of the next review + */ + Date getNextReviewDate(); +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/vital/VitalRecordDefinitionImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/vital/VitalRecordDefinitionImpl.java new file mode 100644 index 0000000000..571992b247 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/vital/VitalRecordDefinitionImpl.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.vital; + +import java.util.Date; + +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.Period; + +/** + * Vital record definition implementation class + * + * @author Roy Wetherall + */ +public class VitalRecordDefinitionImpl implements VitalRecordDefinition, RecordsManagementModel +{ + /** Indicates whether the vital record definition is enabled or not */ + private boolean enabled = false; + + /** Vital record review period */ + private Period reviewPeriod = new Period("none|0"); + + /** + * Constructor. + * + * @param enabled + * @param reviewPeriod + */ + /* package */ VitalRecordDefinitionImpl(boolean enabled, Period reviewPeriod) + { + this.enabled = enabled; + if (reviewPeriod != null) + { + this.reviewPeriod = reviewPeriod; + } + } + + /** + * Helper method to create vital record definition from node reference. + * + * @param nodeService + * @param nodeRef + * @return + */ + /* package */ static VitalRecordDefinition create(NodeService nodeService, NodeRef nodeRef) + { + Boolean enabled = (Boolean)nodeService.getProperty(nodeRef, PROP_VITAL_RECORD_INDICATOR); + Period reviewPeriod = (Period)nodeService.getProperty(nodeRef, PROP_REVIEW_PERIOD); + return new VitalRecordDefinitionImpl(enabled, reviewPeriod); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordDefinition#isEnabled() + */ + @Override + public boolean isEnabled() + { + return enabled; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordDefinition#getNextReviewDate() + */ + public Date getNextReviewDate() + { + return getReviewPeriod().getNextDate(new Date()); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordDefinition#getReviewPeriod() + */ + public Period getReviewPeriod() + { + return reviewPeriod; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/vital/VitalRecordService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/vital/VitalRecordService.java new file mode 100644 index 0000000000..248fd32c77 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/vital/VitalRecordService.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.vital; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.Period; + +/** + * Vital Record Service. + * + * @author Roy Wetherall + * @since 2.0 + */ +public interface VitalRecordService +{ + /** + * Gets the vital record definition details for the node. + * + * @param nodeRef node reference + * @return VitalRecordDefinition vital record definition details + */ + VitalRecordDefinition getVitalRecordDefinition(NodeRef nodeRef); + + /** + * Sets the vital record definition values for a given node. + * + * @param nodeRef + * @param enabled + * @param reviewPeriod + * @return + */ + VitalRecordDefinition setVitalRecordDefintion(NodeRef nodeRef, boolean enabled, Period reviewPeriod); + + /** + * Indicates whether the record is a vital one or not. + * + * @param nodeRef node reference + * @return boolean true if this is a vital record, false otherwise + */ + boolean isVitalRecord(NodeRef nodeRef); + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/vital/VitalRecordServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/vital/VitalRecordServiceImpl.java new file mode 100644 index 0000000000..ef8935bb87 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/vital/VitalRecordServiceImpl.java @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.vital; + +import java.io.Serializable; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.FilePlanComponentKind; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.policy.Behaviour.NotificationFrequency; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.Period; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ParameterCheck; + +/** + * Vital record service interface implementation. + * + * @author Roy Wetherall + * @since 2.0 + */ +public class VitalRecordServiceImpl implements VitalRecordService, + RecordsManagementModel, + NodeServicePolicies.OnUpdatePropertiesPolicy, + NodeServicePolicies.OnAddAspectPolicy +{ + /** Services */ + private NodeService nodeService; + private PolicyComponent policyComponent; + private RecordsManagementService rmService; + private RecordsManagementActionService rmActionService; + + /** Behaviours */ + private JavaBehaviour onUpdateProperties; + private JavaBehaviour onAddAspect; + + /** + * @param nodeService node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @param policyComponent policy component + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * @param rmService records management service + */ + public void setRecordsManagementService(RecordsManagementService rmService) + { + this.rmService = rmService; + } + + /** + * @param rmActionService records management action service + */ + public void setRecordsManagementActionService(RecordsManagementActionService rmActionService) + { + this.rmActionService = rmActionService; + } + + /** + * Init method. + */ + public void init() + { + onUpdateProperties = new JavaBehaviour(this, "onUpdateProperties", NotificationFrequency.TRANSACTION_COMMIT); + policyComponent.bindClassBehaviour( + NodeServicePolicies.OnUpdatePropertiesPolicy.QNAME, + ASPECT_VITAL_RECORD_DEFINITION, + onUpdateProperties); + + onAddAspect = new JavaBehaviour(this, "onAddAspect", NotificationFrequency.TRANSACTION_COMMIT); + policyComponent.bindClassBehaviour( + NodeServicePolicies.OnAddAspectPolicy.QNAME, + ASPECT_VITAL_RECORD_DEFINITION, + onAddAspect); + } + + /** + * @see org.alfresco.repo.node.NodeServicePolicies.OnUpdatePropertiesPolicy#onUpdateProperties(org.alfresco.service.cmr.repository.NodeRef, java.util.Map, java.util.Map) + */ + @Override + public void onUpdateProperties(NodeRef nodeRef, Map before, Map after) + { + if (nodeService.exists(nodeRef) == true) + { + rmActionService.executeRecordsManagementAction(nodeRef, "broadcastVitalRecordDefinition"); + } + } + + /** + * @see org.alfresco.repo.node.NodeServicePolicies.OnAddAspectPolicy#onAddAspect(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + @Override + public void onAddAspect(final NodeRef nodeRef, final QName aspectTypeQName) + { + ParameterCheck.mandatory("nodeRef", nodeRef); + ParameterCheck.mandatory("aspectTypeQName", aspectTypeQName); + + if (nodeService.exists(nodeRef) == true) + { + onUpdateProperties.disable(); + try + { + AuthenticationUtil.runAs(new RunAsWork() + { + public Void doWork() throws Exception + { + // get the immediate parent + NodeRef parentRef = nodeService.getPrimaryParent(nodeRef).getParentRef(); + + // is the parent a record category + if (parentRef != null && + FilePlanComponentKind.RECORD_CATEGORY.equals(rmService.getFilePlanComponentKind(parentRef)) == true) + { + // is the child a record category or folder + FilePlanComponentKind kind = rmService.getFilePlanComponentKind(nodeRef); + if (kind.equals(FilePlanComponentKind.RECORD_CATEGORY) == true || + kind.equals(FilePlanComponentKind.RECORD_FOLDER) == true) + { + // set the vital record definition values to match that of the parent + nodeService.setProperty(nodeRef, + PROP_VITAL_RECORD_INDICATOR, + nodeService.getProperty(parentRef, PROP_VITAL_RECORD_INDICATOR)); + nodeService.setProperty(nodeRef, + PROP_REVIEW_PERIOD, + nodeService.getProperty(parentRef, PROP_REVIEW_PERIOD)); + } + } + + return null; + } + }, AuthenticationUtil.getSystemUserName()); + } + finally + { + onUpdateProperties.enable(); + } + } + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#getVitalRecordDefinition(org.alfresco.service.cmr.repository.NodeRef) + */ + public VitalRecordDefinition getVitalRecordDefinition(NodeRef nodeRef) + { + VitalRecordDefinition result = null; + + FilePlanComponentKind kind = rmService.getFilePlanComponentKind(nodeRef); + if (FilePlanComponentKind.RECORD.equals(kind) == true) + { + result = resolveVitalRecordDefinition(nodeRef); + } + else + { + if (nodeService.hasAspect(nodeRef, ASPECT_VITAL_RECORD_DEFINITION) == true) + { + result = VitalRecordDefinitionImpl.create(nodeService, nodeRef); + } + } + + return result; + } + + /** + * Resolves the record vital definition. + *

+ * NOTE: Currently we only support the resolution of the vital record definition from the + * primary record parent. ie the record folder the record was originally filed within. + *

+ * TODO: Add an algorithm to resolve the correct vital record definition when a record is filed in many + * record folders. + * + * @param record + * @return VitalRecordDefinition + */ + private VitalRecordDefinition resolveVitalRecordDefinition(NodeRef record) + { + NodeRef parent = nodeService.getPrimaryParent(record).getParentRef(); + return getVitalRecordDefinition(parent); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordService#setVitalRecordDefintion(org.alfresco.service.cmr.repository.NodeRef, boolean, org.alfresco.service.cmr.repository.Period) + */ + @Override + public VitalRecordDefinition setVitalRecordDefintion(NodeRef nodeRef, boolean enabled, Period reviewPeriod) + { + // Check params + ParameterCheck.mandatory("nodeRef", nodeRef); + ParameterCheck.mandatory("enabled", enabled); + + // Set the properties (will automatically add the vital record definition aspect) + nodeService.setProperty(nodeRef, PROP_VITAL_RECORD_INDICATOR, enabled); + nodeService.setProperty(nodeRef, PROP_REVIEW_PERIOD, reviewPeriod); + + return new VitalRecordDefinitionImpl(enabled, reviewPeriod); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#isVitalRecord(org.alfresco.service.cmr.repository.NodeRef) + */ + public boolean isVitalRecord(NodeRef nodeRef) + { + return nodeService.hasAspect(nodeRef, ASPECT_VITAL_RECORD); + } +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/CapabilitiesTestSuite.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/CapabilitiesTestSuite.java new file mode 100644 index 0000000000..a21edd0d7c --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/CapabilitiesTestSuite.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.alfresco.module.org_alfresco_module_rm.test.capabilities.CapabilitiesTest; +import org.alfresco.module.org_alfresco_module_rm.test.capabilities.DeclarativeCapabilityTest; +import org.alfresco.module.org_alfresco_module_rm.test.capabilities.CompositeCapabilityTest; + + +/** + * RM test suite + * + * @author Roy Wetherall + */ +public class CapabilitiesTestSuite extends TestSuite +{ + /** + * Creates the test suite + * + * @return the test suite + */ + public static Test suite() + { + TestSuite suite = new TestSuite(); + suite.addTestSuite(CapabilitiesTest.class); + suite.addTestSuite(DeclarativeCapabilityTest.class); + suite.addTestSuite(CompositeCapabilityTest.class); + return suite; + } +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/DOD5015SystemTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/DOD5015SystemTest.java new file mode 100644 index 0000000000..0c406b31e0 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/DOD5015SystemTest.java @@ -0,0 +1,4582 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test; + +import java.io.File; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.transaction.UserTransaction; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionResult; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; +import org.alfresco.module.org_alfresco_module_rm.action.impl.BroadcastDispositionActionDefinitionUpdateAction; +import org.alfresco.module.org_alfresco_module_rm.action.impl.CompleteEventAction; +import org.alfresco.module.org_alfresco_module_rm.action.impl.EditDispositionActionAsOfDateAction; +import org.alfresco.module.org_alfresco_module_rm.action.impl.EditReviewAsOfDateAction; +import org.alfresco.module.org_alfresco_module_rm.action.impl.FileAction; +import org.alfresco.module.org_alfresco_module_rm.action.impl.FreezeAction; +import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; +import org.alfresco.module.org_alfresco_module_rm.caveat.RMCaveatConfigService; +import org.alfresco.module.org_alfresco_module_rm.caveat.RMListOfValuesConstraint.MatchLogic; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.module.org_alfresco_module_rm.dod5015.DOD5015Model; +import org.alfresco.module.org_alfresco_module_rm.event.EventCompletionDetails; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementCustomModel; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementSearchBehaviour; +import org.alfresco.module.org_alfresco_module_rm.test.util.TestUtilities; +import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordDefinition; +import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordService; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.node.integrity.IntegrityException; +import org.alfresco.repo.search.impl.lucene.AbstractLuceneQueryParser; +import org.alfresco.repo.search.impl.lucene.fts.FullTextSearchIndexer; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.security.permissions.AccessDeniedException; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.Period; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.security.PublicServiceAccessService; +import org.alfresco.service.cmr.site.SiteVisibility; +import org.alfresco.service.cmr.view.ImporterService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.BaseSpringTest; +import org.alfresco.util.GUID; +import org.alfresco.util.PropertyMap; + +/** + * DOD System Test + * + * @author Roy Wetherall, Neil McErlean + */ +public class DOD5015SystemTest extends BaseSpringTest implements RecordsManagementModel, DOD5015Model +{ + private static final Period weeklyReview = new Period("week|1"); + private static final Period dailyReview = new Period("day|1"); + public static final long TWENTY_FOUR_HOURS_IN_MS = 24 * 60 * 60 * 1000; // hours * minutes * seconds * millis + + protected static StoreRef SPACES_STORE = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore"); + + private NodeRef filePlan; + + private NodeService unprotectedNodeService; + private NodeService nodeService; + private SearchService searchService; + private ImporterService importService; + private ContentService contentService; + private RecordsManagementService rmService; + private RecordsManagementActionService rmActionService; + private ServiceRegistry serviceRegistry; + private TransactionService transactionService; + private RecordsManagementAdminService rmAdminService; + private RMCaveatConfigService caveatConfigService; + private DispositionService dispositionService; + private VitalRecordService vitalRecordService; + + private MutableAuthenticationService authenticationService; + private PersonService personService; + private AuthorityService authorityService; + private PermissionService permissionService; + private RetryingTransactionHelper transactionHelper; + + private PublicServiceAccessService publicServiceAccessService; + private FullTextSearchIndexer luceneFTS; + + // example base test data for supplemental markings list (see also recordsModel.xml) + protected final static String NOFORN = "NOFORN"; // Not Releasable to Foreign Nationals/Governments/Non-US Citizens + protected final static String NOCONTRACT = "NOCONTRACT"; // Not Releasable to Contractors or Contractor/Consultants + protected final static String FOUO = "FOUO"; // For Official Use Only + protected final static String FGI = "FGI"; // Foreign Government Information + + // example user-defined field + protected final static QName CONSTRAINT_CUSTOM_PRJLIST = QName.createQName(RM_CUSTOM_URI, "prjList"); + protected final static QName PROP_CUSTOM_PRJLIST = QName.createQName(RM_CUSTOM_URI, "projectNameList"); + + protected final static String PRJ_A = "Project A"; + protected final static String PRJ_B = "Project B"; + protected final static String PRJ_C = "Project C"; + + @Override + protected void onSetUpInTransaction() throws Exception + { + super.onSetUpInTransaction(); + + // Get the service required in the tests + this.unprotectedNodeService = (NodeService)applicationContext.getBean("nodeService"); + this.nodeService = (NodeService)this.applicationContext.getBean("NodeService"); // use upper 'N'odeService (to test access config interceptor) NodeService unprotectedNodeService = (NodeService)applicationContext.getBean("nodeService"); + this.authenticationService = (MutableAuthenticationService)this.applicationContext.getBean("AuthenticationService"); + this.personService = (PersonService)this.applicationContext.getBean("PersonService"); + this.authorityService = (AuthorityService)this.applicationContext.getBean("AuthorityService"); + this.permissionService = (PermissionService)this.applicationContext.getBean("PermissionService"); + this.searchService = (SearchService)this.applicationContext.getBean("SearchService"); // use upper 'S'earchService (to test access config interceptor) + this.importService = (ImporterService)this.applicationContext.getBean("importerComponent"); + this.contentService = (ContentService)this.applicationContext.getBean("ContentService"); + this.rmService = (RecordsManagementService)this.applicationContext.getBean("RecordsManagementService"); + this.rmActionService = (RecordsManagementActionService)this.applicationContext.getBean("RecordsManagementActionService"); + this.serviceRegistry = (ServiceRegistry)this.applicationContext.getBean("ServiceRegistry"); + this.transactionService = (TransactionService)this.applicationContext.getBean("TransactionService"); + this.rmAdminService = (RecordsManagementAdminService)this.applicationContext.getBean("RecordsManagementAdminService"); + this.caveatConfigService = (RMCaveatConfigService)this.applicationContext.getBean("caveatConfigService"); + this.publicServiceAccessService = (PublicServiceAccessService)this.applicationContext.getBean("PublicServiceAccessService"); + this.transactionHelper = (RetryingTransactionHelper)this.applicationContext.getBean("retryingTransactionHelper"); + this.dispositionService = (DispositionService)this.applicationContext.getBean("DispositionService"); + this.luceneFTS = (FullTextSearchIndexer)this.applicationContext.getBean("LuceneFullTextSearchIndexer"); + this.vitalRecordService = (VitalRecordService)applicationContext.getBean("VitalRecordService"); + + + // Set the current security context as admin + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + + // Get the test data + filePlan = TestUtilities.loadFilePlanData(applicationContext); + + File file = new File(System.getProperty("user.dir")+"/test-resources/testCaveatConfig1.json"); // from test-resources + assertTrue(file.exists()); + + caveatConfigService.updateOrCreateCaveatConfig(file); + + // set/reset allowed values (empty list by default) + List newValues = new ArrayList(4); + newValues.add(NOFORN); + newValues.add(NOCONTRACT); + newValues.add(FOUO); + newValues.add(FGI); + + rmAdminService.changeCustomConstraintValues(RecordsManagementCustomModel.CONSTRAINT_CUSTOM_SMLIST, newValues); + + // We pause FTS during this test, as it moves around records in intermediate places, and otherwise FTS may not + // finish clearing up its mess before each test finishes + this.luceneFTS.pause(); + } + + + + /* (non-Javadoc) + * @see org.springframework.test.AbstractTransactionalSpringContextTests#onTearDown() + */ + @Override + protected void onTearDown() throws Exception + { + super.onTearDown(); + + // Let FTS catch up again. + this.luceneFTS.resume(); + } + + + + /** + * Tests that the test data has been loaded correctly + */ + public void xtestTestData() throws Exception + { + // make sure the folders that should have disposition schedules do so + NodeRef janAuditRecordsFolder = TestUtilities.getRecordFolder(rmService, nodeService, "Reports", "AIS Audit Records", "January AIS Audit Records"); + assertNotNull(janAuditRecordsFolder); + + // ensure the folder has the disposition lifecycle aspect + assertTrue("Expected 'January AIS Audit Records' folder to have disposition lifecycle aspect applied", + nodeService.hasAspect(janAuditRecordsFolder, ASPECT_DISPOSITION_LIFECYCLE)); + + // ensure the folder has the correctly setup search aspect + checkSearchAspect(janAuditRecordsFolder); + + // check another folder that has events as part of the disposition schedule + NodeRef equalOppCoordFolder = TestUtilities.getRecordFolder(rmService, nodeService, "Military Files", "Personnel Security Program Records", "Equal Opportunity Coordinator"); + assertNotNull(equalOppCoordFolder); + assertTrue("Expected 'Equal Opportunity Coordinator' folder to have disposition lifecycle aspect applied", + nodeService.hasAspect(equalOppCoordFolder, ASPECT_DISPOSITION_LIFECYCLE)); + checkSearchAspect(equalOppCoordFolder); + } + + /** + * This test method creates a non-vital record and then moves it to a vital folder + * (triggering a refile) and then moves it a second time to another vital record + * having different metadata. + * + * Moving a Record within the FilePlan should trigger a "refile". Refiling a record + * will lead to the reconsideration of its disposition, vital and transfer/accession + * metadata, with potential changes therein. + */ + public void testMoveRefileRecord() throws Exception + { + // Commit in order to trigger the setUpRecordFolder behaviour + setComplete(); + endTransaction(); + + final NodeRef nonVitalFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + // Create a record folder under a "non-vital" category + NodeRef nonVitalRecordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Reports", "Unit Manning Documents"); + assertNotNull(nonVitalRecordCategory); + + return createRecFolderNode(nonVitalRecordCategory); + } + }); + + final NodeRef recordUnderTest = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + // Create a (non-vital) record under the above folder + NodeRef recordUnderTest = createRecordNode(nonVitalFolder); + + rmActionService.executeRecordsManagementAction(recordUnderTest, "file"); + + TestUtilities.declareRecord(recordUnderTest, unprotectedNodeService, rmActionService); + + return recordUnderTest; + } + }); + + final NodeRef vitalFolder =transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + // No need to commit the transaction here as the record is non-vital and + // there is no metadata to copy down. + + NodeRef vitalFolder = retrieveJanuaryAISVitalFolder(); + + // Move the non-vital record under the vital folder. + serviceRegistry.getFileFolderService().move(recordUnderTest, vitalFolder, null); + + return vitalFolder; + } + }); + + final NodeRef secondVitalFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + // At this point, the formerly nonVitalRecord is now actually vital. + assertTrue("Expected record.", rmService.isRecord(recordUnderTest)); + assertTrue("Expected declared.", rmService.isRecordDeclared(recordUnderTest)); + + final VitalRecordDefinition recordVrd = vitalRecordService.getVitalRecordDefinition(recordUnderTest); + assertNotNull("Moved record should now have a Vital Rec Defn", recordVrd); + assertEquals("Moved record had wrong review period", + vitalRecordService.getVitalRecordDefinition(vitalFolder).getReviewPeriod(), recordVrd.getReviewPeriod()); + assertNotNull("Moved record should now have a review-as-of date", nodeService.getProperty(recordUnderTest, PROP_REVIEW_AS_OF)); + + // Create another folder with different vital/disposition instructions + //TODO Change disposition instructions + NodeRef vitalRecordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Reports", "AIS Audit Records"); + assertNotNull(vitalRecordCategory); + return createRecFolderNode(vitalRecordCategory); + } + }); + + final Date reviewDate = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Date execute() throws Throwable + { + Map props = nodeService.getProperties(secondVitalFolder); + final Serializable secondVitalFolderReviewPeriod = props.get(PROP_REVIEW_PERIOD); + assertEquals("Unexpected review period.", weeklyReview, secondVitalFolderReviewPeriod); + + // We are changing the review period of this second record folder. + nodeService.setProperty(secondVitalFolder, PROP_REVIEW_PERIOD, dailyReview); + + Date reviewDate = (Date)nodeService.getProperty(recordUnderTest, PROP_REVIEW_AS_OF); + + // Move the newly vital record under the second vital folder. I expect the reviewPeriod + // for the record to be changed again. + serviceRegistry.getFileFolderService().move(recordUnderTest, secondVitalFolder, null); + + return reviewDate; + } + }); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + Period newReviewPeriod = vitalRecordService.getVitalRecordDefinition(recordUnderTest).getReviewPeriod(); + assertEquals("Unexpected review period.", dailyReview, newReviewPeriod); + + Date updatedReviewDate = (Date)nodeService.getProperty(recordUnderTest, PROP_REVIEW_AS_OF); + // The reviewAsOf date should have changed to "24 hours from now". + assertFalse("reviewAsOf date was unchanged", reviewDate.equals(updatedReviewDate)); + long millisecondsUntilNextReview = updatedReviewDate.getTime() - new Date().getTime(); + assertTrue("new reviewAsOf date was not within 24 hours of now.", + millisecondsUntilNextReview <= TWENTY_FOUR_HOURS_IN_MS); + + nodeService.deleteNode(recordUnderTest); + nodeService.deleteNode(nonVitalFolder); + nodeService.deleteNode(secondVitalFolder); + + return null; + } + }); + } + + public void off_testMoveRefileRecordFolder() throws Exception + { + //TODO Impl me + fail("Not yet impl'd."); + } + + public void off_testCopyRefileRecordFolder() throws Exception + { + //TODO Impl me + fail("Not yet impl'd."); + } + + public void off_testCopyRefileRecord() throws Exception + { + //TODO Impl me + fail("Not yet impl'd."); + } + + private NodeRef createRecordCategoryNode(NodeRef parentRecordSeries) + { + NodeRef newCategory = this.nodeService.createNode(parentRecordSeries, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "Test category " + System.currentTimeMillis()), + TYPE_RECORD_CATEGORY).getChildRef(); + + return newCategory; + } + + private NodeRef createRecFolderNode(NodeRef parentRecordCategory) + { + NodeRef newFolder = this.nodeService.createNode(parentRecordCategory, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "Test folder " + System.currentTimeMillis()), + TYPE_RECORD_FOLDER).getChildRef(); + return newFolder; + } + + private NodeRef createRecordNode(NodeRef parentFolder) + { + NodeRef newRecord = this.nodeService.createNode(parentFolder, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, + "Record" + System.currentTimeMillis() + ".txt"), + ContentModel.TYPE_CONTENT).getChildRef(); + ContentWriter writer = this.contentService.getWriter(newRecord, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent("Irrelevant content"); + return newRecord; + } + + private NodeRef retrieveJanuaryAISVitalFolder() + { + final List resultNodeRefs = retrieveJanuaryAISVitalFolders(); + final int folderCount = resultNodeRefs.size(); +// assertTrue("There should only be one 'January AIS Audit Records' folder. Were " + folderCount, folderCount == 1); + + // This nodeRef should have rma:VRI=true, rma:reviewPeriod=week|1, rma:isClosed=false + return resultNodeRefs.get(0); + } + + private List retrieveJanuaryAISVitalFolders() + { + String typeQuery = "TYPE:\"" + TYPE_RECORD_FOLDER + "\" AND @cm\\:name:\"January AIS Audit Records\""; + ResultSet types = this.searchService.query(SPACES_STORE, SearchService.LANGUAGE_LUCENE, typeQuery); + + final List resultNodeRefs = types.getNodeRefs(); + types.close(); + return resultNodeRefs; + } + + /** + * Test duplicate id's + */ + public void xxtestDuplicateIDs() + { + List roots = rmService.getFilePlans(); + final NodeRef root = roots.get(0); + setComplete(); + endTransaction(); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + String name1 = GUID.generate(); + Map props = new HashMap(2); + props.put(ContentModel.PROP_NAME, name1); + props.put(PROP_IDENTIFIER, "bob"); + ChildAssociationRef assoc = nodeService.createNode( + root, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name1), + TYPE_RECORD_CATEGORY, + props); + + return assoc.getChildRef(); + } + }); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + try + { + String name1 = GUID.generate(); + Map props = new HashMap(2); + props.put(ContentModel.PROP_NAME, name1); + props.put(PROP_IDENTIFIER, "bob"); + ChildAssociationRef assoc = nodeService.createNode( + root, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name1), + TYPE_RECORD_CATEGORY, + props); + fail("Cant duplicate series id"); + } + catch (Exception e) + { + // expected + } + + return null; + } + }); + } + + public void testDispositionLifecycle_0318_01_basictest() throws Exception + { + final NodeRef recordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Reports", "AIS Audit Records"); + setComplete(); + endTransaction(); + + final NodeRef recordFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + + assertNotNull(recordCategory); + assertEquals("AIS Audit Records", nodeService.getProperty(recordCategory, ContentModel.PROP_NAME)); + + return createRecordFolder(recordCategory, "March AIS Audit Records"); + } + }); + + final NodeRef recordOne = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + // Check the folder to ensure everything has been inherited correctly + assertTrue(((Boolean)nodeService.getProperty(recordFolder, PROP_VITAL_RECORD_INDICATOR)).booleanValue()); + assertEquals(nodeService.getProperty(recordCategory, PROP_REVIEW_PERIOD), + nodeService.getProperty(recordFolder, PROP_REVIEW_PERIOD)); + + // Create the document + Map props = new HashMap(1); + props.put(ContentModel.PROP_NAME, "MyRecord.txt"); + NodeRef recordOne = nodeService.createNode(recordFolder, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "MyRecord.txt"), + ContentModel.TYPE_CONTENT).getChildRef(); + + // Set the content + ContentWriter writer = contentService.getWriter(recordOne, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent("There is some content in this record"); + + return recordOne; + } + }); + + // Checked that the document has been marked as incomplete + System.out.println("recordOne ..."); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + assertTrue(nodeService.hasAspect(recordOne, ASPECT_RECORD)); + assertNotNull(nodeService.getProperty(recordOne, PROP_IDENTIFIER)); + System.out.println("Record id: " + nodeService.getProperty(recordOne, PROP_IDENTIFIER)); + assertNotNull(nodeService.getProperty(recordOne, PROP_DATE_FILED)); + System.out.println("Date filed: " + nodeService.getProperty(recordOne, PROP_DATE_FILED)); + + // Check the review schedule + assertTrue(nodeService.hasAspect(recordOne, ASPECT_VITAL_RECORD)); + assertNotNull(nodeService.getProperty(recordOne, PROP_REVIEW_AS_OF)); + System.out.println("Review as of: " + nodeService.getProperty(recordOne, PROP_REVIEW_AS_OF)); + + // Change the review asOf date + Date nowDate = new Date(); + assertFalse(nowDate.equals(nodeService.getProperty(recordOne, PROP_REVIEW_AS_OF))); + Map reviewAsOfParams = new HashMap(1); + reviewAsOfParams.put(EditReviewAsOfDateAction.PARAM_AS_OF_DATE, nowDate); + rmActionService.executeRecordsManagementAction(recordOne, "editReviewAsOfDate", reviewAsOfParams); + assertTrue(nowDate.equals(nodeService.getProperty(recordOne, PROP_REVIEW_AS_OF))); + + // NOTE the disposition is being managed at a folder level ... + + // Check the disposition action + assertFalse(nodeService.hasAspect(recordOne, ASPECT_DISPOSITION_LIFECYCLE)); + assertTrue(nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE)); + + NodeRef ndNodeRef = nodeService.getChildAssocs(recordFolder, ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + assertNotNull(ndNodeRef); + + assertNotNull(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION_ID)); + System.out.println("Disposition action id: " + nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION_ID)); + assertEquals("cutoff", nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION)); + System.out.println("Disposition action: " + nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION)); + assertNotNull(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF)); + System.out.println("Disposition as of: " + nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF)); + + // Check for the search properties having been populated + checkSearchAspect(recordFolder); + + // Test the declaration of a record by editing properties + Map propValues = new HashMap(); + propValues.put(RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + List smList = new ArrayList(2); + smList.add(FOUO); + smList.add(NOFORN); + propValues.put(RecordsManagementModel.PROP_SUPPLEMENTAL_MARKING_LIST, (Serializable)smList); + propValues.put(RecordsManagementModel.PROP_MEDIA_TYPE, "mediaTypeValue"); + propValues.put(RecordsManagementModel.PROP_FORMAT, "formatValue"); + propValues.put(RecordsManagementModel.PROP_DATE_RECEIVED, new Date()); + nodeService.addProperties(recordOne, propValues); + + return null; + } + }); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // Try and declare, expected failure + try + { + rmActionService.executeRecordsManagementAction(recordOne, "declareRecord"); + fail("Should not be able to declare a record that still has mandatory properties unset"); + } + catch (Exception e) + { + // Expected + } + + return null; + } + }); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @SuppressWarnings("deprecation") + public Object execute() throws Throwable + { + assertTrue("Before test DECLARED aspect was set", + nodeService.hasAspect(recordOne, ASPECT_DECLARED_RECORD) == false); + + nodeService.setProperty(recordOne, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + nodeService.setProperty(recordOne, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + nodeService.setProperty(recordOne, ContentModel.PROP_TITLE, "titleValue"); + + // Declare the record as we have set everything we should have + rmActionService.executeRecordsManagementAction(recordOne, "declareRecord"); + assertTrue(" the record is not declared", nodeService.hasAspect(recordOne, ASPECT_DECLARED_RECORD)); + + // check that the declaredAt and declaredBy properties are set + assertNotNull(nodeService.getProperty(recordOne, PROP_DECLARED_BY)); + assertEquals("admin", nodeService.getProperty(recordOne, PROP_DECLARED_BY)); + assertNotNull(nodeService.getProperty(recordOne, PROP_DECLARED_AT)); + Date dateNow = new Date(); + Date declaredDate = (Date)nodeService.getProperty(recordOne, PROP_DECLARED_AT); + assertEquals(declaredDate.getDate(), dateNow.getDate()); + assertEquals(declaredDate.getMonth(), dateNow.getMonth()); + assertEquals(declaredDate.getYear(), dateNow.getYear()); + + // Check that the history is empty + List history = dispositionService.getCompletedDispositionActions(recordFolder); + assertNotNull(history); + assertEquals(0, history.size()); + + return null; + } + }); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // Execute the cutoff action (should fail because this is being done at the record level) + try + { + rmActionService.executeRecordsManagementAction(recordFolder, "cutoff", null); + fail(("Shouldn't have been able to execute cut off at the record level")); + } + catch (Exception e) + { + // expected + } + + // Execute the cutoff action (should fail becuase it is not yet eligiable) + try + { + rmActionService.executeRecordsManagementAction(recordFolder, "cutoff", null); + fail(("Shouldn't have been able to execute because it is not yet eligiable")); + } + catch (Exception e) + { + // expected + } + + return null; + } + }); + + final Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // Clock the asOf date back to ensure eligibility + NodeRef ndNodeRef = nodeService.getChildAssocs(recordFolder, ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + Date nowDate = calendar.getTime(); + assertFalse(nowDate.equals(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF))); + Map params = new HashMap(1); + params.put(EditDispositionActionAsOfDateAction.PARAM_AS_OF_DATE, nowDate); + rmActionService.executeRecordsManagementAction(recordFolder, "editDispositionActionAsOfDate", params); + assertTrue(nowDate.equals(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF))); + + // Cut off + rmActionService.executeRecordsManagementAction(recordFolder, "cutoff", null); + + return null; + } + }); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // Check the disposition action + assertFalse(nodeService.hasAspect(recordOne, ASPECT_DISPOSITION_LIFECYCLE)); + assertTrue(nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE)); + + NodeRef ndNodeRef = nodeService.getChildAssocs(recordFolder, ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + assertNotNull(ndNodeRef); + + assertNotNull(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION_ID)); + System.out.println("Disposition action id: " + nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION_ID)); + assertEquals("destroy", nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION)); + System.out.println("Disposition action: " + nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION)); + assertNotNull(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF)); + System.out.println("Disposition as of: " + nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF)); + assertNull(nodeService.getProperty(recordFolder, RecordsManagementSearchBehaviour.PROP_RS_DISPOSITION_EVENTS)); + + // Check the previous action details + checkLastDispositionAction(recordFolder, "cutoff", 1); + + // Check for the search properties having been populated + checkSearchAspect(recordFolder); + + // Clock the asOf date back to ensure eligibility + ndNodeRef = nodeService.getChildAssocs(recordFolder, ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + Date nowDate = calendar.getTime(); + assertFalse(nowDate.equals(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF))); + Map params = new HashMap(1); + params.put(EditDispositionActionAsOfDateAction.PARAM_AS_OF_DATE, nowDate); + rmActionService.executeRecordsManagementAction(recordFolder, "editDispositionActionAsOfDate", params); + assertTrue(nowDate.equals(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF))); + + rmActionService.executeRecordsManagementAction(recordFolder, "destroy", null); + + // Check that the node has been destroyed (ghosted) + //assertFalse(nodeService.exists(recordFolder)); + //assertFalse(nodeService.exists(recordOne)); + assertTrue(nodeService.hasAspect(recordFolder, ASPECT_GHOSTED)); + assertTrue(nodeService.hasAspect(recordOne, ASPECT_GHOSTED)); + + // Check the history + if (nodeService.exists(recordFolder) == true) + { + checkLastDispositionAction(recordFolder, "destroy", 2); + } + + return null; + } + }); + } + + /** + * Tests the re-scheduling of disposition lifecycles when the schedule changes + */ + public void testDispositionLifecycle_0318_reschedule_folderlevel() throws Exception + { + final NodeRef recordSeries = TestUtilities.getRecordSeries(rmService, nodeService, "Reports"); + setComplete(); + endTransaction(); + + // create a category + final NodeRef recordCategory = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + assertNotNull(recordSeries); + assertEquals("Reports", nodeService.getProperty(recordSeries, ContentModel.PROP_NAME)); + + return createRecordCategoryNode(recordSeries); + } + }); + + // define the disposition schedule for the category (Cut off monthly, hold 1 month, then destroy) + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + assertNotNull(recordCategory); + + DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); + assertNotNull(schedule); + nodeService.setProperty(schedule.getNodeRef(), PROP_DISPOSITION_INSTRUCTIONS, "Cutoff after 1 month then destroy after 1 month"); + nodeService.setProperty(schedule.getNodeRef(), PROP_DISPOSITION_AUTHORITY, "Alfresco"); + + // define properties for both steps + Map step1 = new HashMap(); + step1.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, "cutoff"); + step1.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, "Cutoff after 1 month"); + step1.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "month|1"); + + Map step2 = new HashMap(); + step2.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, "destroy"); + step2.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, "Destroy after 1 month"); + step2.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "month|1"); + step2.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD_PROPERTY, PROP_CUT_OFF_DATE); + + // add the action definitions to the schedule + dispositionService.addDispositionActionDefinition(schedule, step1); + dispositionService.addDispositionActionDefinition(schedule, step2); + + return null; + } + }); + + // create a record folder + final NodeRef recordFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + return createRecordFolder(recordCategory, "Folder1"); + } + }); + + // make sure the disposition lifecycle is present and correct + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @SuppressWarnings("deprecation") + public Object execute() throws Throwable + { + assertNotNull(recordFolder); + + assertTrue(nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE)); + NodeRef ndNodeRef = nodeService.getChildAssocs(recordFolder, ASSOC_NEXT_DISPOSITION_ACTION, + RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + assertNotNull(ndNodeRef); + + assertNotNull(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION_ID)); + System.out.println("Disposition action id: " + nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION_ID)); + assertEquals("cutoff", nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION)); + System.out.println("Disposition action: " + nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION)); + assertNotNull(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF)); + Date asOfDate = (Date)nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF); + System.out.println("Disposition as of: " + asOfDate); + + // make sure the as of date is a month in the future + Calendar cal = Calendar.getInstance(); + cal.add(Calendar.MONTH, 1); + int monthThen = cal.get(Calendar.MONTH); + assertEquals(asOfDate.getMonth(), monthThen);; + + // make sure there aren't any events + List events = nodeService.getChildAssocs(ndNodeRef, ASSOC_EVENT_EXECUTIONS, + RegexQNamePattern.MATCH_ALL); + assertEquals(0, events.size()); + + // Check for the search properties having been populated + checkSearchAspect(recordFolder); + + return null; + } + }); + + // change the period on the 1st step of the disposition schedule and make sure it perculates down + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // define changes for schedule + Map changes = new HashMap(); + changes.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "month|3"); + + // update the second dispostion action definition + DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); + assertNotNull(schedule); + List actionDefs = schedule.getDispositionActionDefinitions(); + assertEquals(2, actionDefs.size()); + System.out.println("Adding 3 months to period for 1st step: " + actionDefs.get(0).getName()); + updateDispositionActionDefinition(schedule, actionDefs.get(0), changes); + + return null; + } + }); + + // make sure the disposition lifecycle asOf date has been updated + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @SuppressWarnings("deprecation") + public Object execute() throws Throwable + { + assertTrue(nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE)); + NodeRef ndNodeRef = nodeService.getChildAssocs(recordFolder, ASSOC_NEXT_DISPOSITION_ACTION, + RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + assertNotNull(ndNodeRef); + + assertNotNull(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF)); + Date asOfDate = (Date)nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF); + System.out.println("Disposition as of: " + asOfDate); + + // make sure the as of date is a month in the future + Calendar calendar = Calendar.getInstance(); + calendar.add(Calendar.MONTH, 3); + System.out.println("Test date: " + calendar.getTime()); + Calendar asOfCalendar = Calendar.getInstance(); + asOfCalendar.setTime(asOfDate); + assertEquals(calendar.get(Calendar.MONTH), asOfCalendar.get(Calendar.MONTH)); + + // Check for the search properties having been populated + checkSearchAspect(recordFolder); + + return null; + } + }); + + // change the period on the 2nd step of the disposition schedule and make sure it DOES NOT perculate down + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // define changes for schedule + Map changes = new HashMap(); + changes.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "month|6"); + + // update the first dispostion action definition + DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); + assertNotNull(schedule); + List actionDefs = schedule.getDispositionActionDefinitions(); + assertEquals(2, actionDefs.size()); + System.out.println("Adding 6 months to period for 2nd step: " + actionDefs.get(1).getName()); + updateDispositionActionDefinition(schedule, actionDefs.get(1), changes); + + return null; + } + }); + + // make sure the disposition lifecycle asOf date has NOT been updated as the period was + // changed for a step other than the current one + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @SuppressWarnings("deprecation") + public Object execute() throws Throwable + { + assertTrue(nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE)); + NodeRef ndNodeRef = nodeService.getChildAssocs(recordFolder, ASSOC_NEXT_DISPOSITION_ACTION, + RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + assertNotNull(ndNodeRef); + + assertNotNull(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF)); + Date asOfDate = (Date)nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF); + System.out.println("Disposition as of: " + asOfDate); + + // make sure the as of date is a month in the future + Calendar calendar = Calendar.getInstance(); + calendar.add(Calendar.MONTH, 3); + assertEquals("Expecting the asOf date to be unchanged",asOfDate.getMonth(), calendar.get(Calendar.MONTH)); + + // Check for the search properties having been populated + checkSearchAspect(recordFolder); + + return null; + } + }); + + // change the disposition schedule to be event based rather than time based i.e. + // remove the period properties and supply 2 events in its place. + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // define changes for schedule + Map changes = new HashMap(); + changes.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, null); + List events = new ArrayList(2); + events.add("no_longer_needed"); + events.add("case_complete"); + changes.put(RecordsManagementModel.PROP_DISPOSITION_EVENT, (Serializable)events); + + // update the first dispostion action definition + DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); + assertNotNull(schedule); + List actionDefs = schedule.getDispositionActionDefinitions(); + assertEquals(2, actionDefs.size()); + System.out.println("Removing period and adding no_longer_needed and case_complete to 1st step: " + + actionDefs.get(0).getName()); + updateDispositionActionDefinition(schedule, actionDefs.get(0), changes); + + return null; + } + }); + + // make sure the disposition lifecycle asOf date has been reset and there are now + // events hanging off the nextdispositionaction node + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + assertTrue(nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE)); + NodeRef ndNodeRef = nodeService.getChildAssocs(recordFolder, ASSOC_NEXT_DISPOSITION_ACTION, + RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + assertNotNull(ndNodeRef); + + Date asOfDate = (Date)nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF); + System.out.println("New disposition as of: " + asOfDate); + assertNull("Expecting asOfDate to be null", asOfDate); + + // make sure the 2 events are present + List events = nodeService.getChildAssocs(ndNodeRef, ASSOC_EVENT_EXECUTIONS, + RegexQNamePattern.MATCH_ALL); + assertEquals(2, events.size()); + NodeRef event1 = events.get(0).getChildRef(); + assertEquals("no_longer_needed", nodeService.getProperty(event1, PROP_EVENT_EXECUTION_NAME)); + NodeRef event2 = events.get(1).getChildRef(); + assertEquals("case_complete", nodeService.getProperty(event2, PROP_EVENT_EXECUTION_NAME)); + + // Check for the search properties having been populated + checkSearchAspect(recordFolder, false); + + return null; + } + }); + + // remove one of the events just added + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // define changes for schedule + Map changes = new HashMap(); + List events = new ArrayList(2); + events.add("case_complete"); + changes.put(RecordsManagementModel.PROP_DISPOSITION_EVENT, (Serializable)events); + + // update the first dispostion action definition + DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); + assertNotNull(schedule); + List actionDefs = schedule.getDispositionActionDefinitions(); + assertEquals(2, actionDefs.size()); + System.out.println("Removing no_longer_needed event from 1st step: " + + actionDefs.get(0).getName()); + updateDispositionActionDefinition(schedule, actionDefs.get(0), changes); + + return null; + } + }); + + // make sure the disposition lifecycle asOf date is still null and ensure there is only one event + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + assertTrue(nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE)); + NodeRef ndNodeRef = nodeService.getChildAssocs(recordFolder, ASSOC_NEXT_DISPOSITION_ACTION, + RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + assertNotNull(ndNodeRef); + + Date asOfDate = (Date)nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF); + assertNull("Expecting asOfDate to be null", asOfDate); + + // make sure only 1 event is present + List events = nodeService.getChildAssocs(ndNodeRef, ASSOC_EVENT_EXECUTIONS, + RegexQNamePattern.MATCH_ALL); + assertEquals(1, events.size()); + NodeRef event = events.get(0).getChildRef(); + assertEquals("case_complete", nodeService.getProperty(event, PROP_EVENT_EXECUTION_NAME)); + + // Check for the search properties having been populated + checkSearchAspect(recordFolder, false); + + return null; + } + }); + } + + /** + * Tests the re-scheduling of disposition lifecycles when the schedule changes + */ + public void testDispositionLifecycle_0318_reschedule_recordlevel() throws Exception + { + final NodeRef recordSeries = TestUtilities.getRecordSeries(rmService, nodeService, "Reports"); + setComplete(); + endTransaction(); + + // create a category + final NodeRef recordCategory = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + assertNotNull(recordSeries); + assertEquals("Reports", nodeService.getProperty(recordSeries, ContentModel.PROP_NAME)); + + return createRecordCategoryNode(recordSeries); + } + }); + + // define the disposition schedule for the category (Cut off monthly, hold 1 month, then destroy) + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + assertNotNull(recordCategory); + + // get the disposition schedule and turn on record level disposition + DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); + assertNotNull(schedule); + nodeService.setProperty(schedule.getNodeRef(), PROP_DISPOSITION_INSTRUCTIONS, "Cutoff after 1 month then destroy after 1 month"); + nodeService.setProperty(schedule.getNodeRef(), PROP_DISPOSITION_AUTHORITY, "Alfresco"); + nodeService.setProperty(schedule.getNodeRef(), PROP_RECORD_LEVEL_DISPOSITION, true); + + // define properties for both steps + Map step1 = new HashMap(); + step1.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, "cutoff"); + step1.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, "Cutoff after 1 month"); + step1.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "month|1"); + + Map step2 = new HashMap(); + step2.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, "destroy"); + step2.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, "Destroy after 1 month"); + step2.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "month|1"); + step2.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD_PROPERTY, PROP_CUT_OFF_DATE); + + // add the action definitions to the schedule + dispositionService.addDispositionActionDefinition(schedule, step1); + dispositionService.addDispositionActionDefinition(schedule, step2); + + return null; + } + }); + + // create a record folder + final NodeRef recordFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + return createRecordFolder(recordCategory, "Folder1"); + } + }); + + // create a record + final NodeRef record = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + assertNotNull(recordFolder); + + // Create the document + Map props = new HashMap(1); + props.put(ContentModel.PROP_NAME, "MyRecord.txt"); + NodeRef record = nodeService.createNode(recordFolder, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "MyRecord.txt"), + ContentModel.TYPE_CONTENT).getChildRef(); + + // Set the content + ContentWriter writer = contentService.getWriter(record, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent("There is some content in this record"); + + return record; + } + }); + + // make sure the disposition lifecycle is present and correct on the record and not on the folder + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @SuppressWarnings("deprecation") + public Object execute() throws Throwable + { + assertNotNull(record); + + assertFalse(nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE)); + assertTrue(nodeService.hasAspect(record, ASPECT_DISPOSITION_LIFECYCLE)); + NodeRef ndNodeRef = nodeService.getChildAssocs(record, ASSOC_NEXT_DISPOSITION_ACTION, + RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + assertNotNull(ndNodeRef); + + assertNotNull(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION_ID)); + System.out.println("Disposition action id: " + nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION_ID)); + assertEquals("cutoff", nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION)); + System.out.println("Disposition action: " + nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION)); + assertNotNull(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF)); + Date asOfDate = (Date)nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF); + System.out.println("Disposition as of: " + asOfDate); + + // make sure the as of date is a month in the future + Calendar cal = Calendar.getInstance(); + cal.add(Calendar.MONTH, 1); + int monthThen = cal.get(Calendar.MONTH); + assertEquals(asOfDate.getMonth(), monthThen); + + // make sure there aren't any events + List events = nodeService.getChildAssocs(ndNodeRef, ASSOC_EVENT_EXECUTIONS, + RegexQNamePattern.MATCH_ALL); + assertEquals(0, events.size()); + + // Check for the search properties having been populated + checkSearchAspect(record); + + return null; + } + }); + + // change the period on the 1st step of the disposition schedule and make sure it perculates down + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // define changes for schedule + Map changes = new HashMap(); + changes.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "month|3"); + + // update the second dispostion action definition + DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); + assertNotNull(schedule); + List actionDefs = schedule.getDispositionActionDefinitions(); + assertEquals(2, actionDefs.size()); + System.out.println("Adding 3 months to period for 1st step: " + actionDefs.get(0).getName()); + updateDispositionActionDefinition(schedule, actionDefs.get(0), changes); + + return null; + } + }); + + // make sure the disposition lifecycle asOf date has been updated + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @SuppressWarnings("deprecation") + public Object execute() throws Throwable + { + assertTrue(nodeService.hasAspect(record, ASPECT_DISPOSITION_LIFECYCLE)); + NodeRef ndNodeRef = nodeService.getChildAssocs(record, ASSOC_NEXT_DISPOSITION_ACTION, + RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + assertNotNull(ndNodeRef); + + assertNotNull(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF)); + Date asOfDate = (Date)nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF); + System.out.println("Disposition as of: " + asOfDate); + + // make sure the as of date is a month in the future + Calendar calendar = Calendar.getInstance(); + calendar.add(Calendar.MONTH, 3); + assertEquals(asOfDate.getMonth(), calendar.get(Calendar.MONTH)); + + // Check for the search properties having been populated + checkSearchAspect(record); + + return null; + } + }); + + // change the period on the 2nd step of the disposition schedule and make sure it DOES NOT perculate down + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // define changes for schedule + Map changes = new HashMap(); + changes.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "month|6"); + + // update the first dispostion action definition + DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); + assertNotNull(schedule); + List actionDefs = schedule.getDispositionActionDefinitions(); + assertEquals(2, actionDefs.size()); + System.out.println("Adding 6 months to period for 2nd step: " + actionDefs.get(1).getName()); + updateDispositionActionDefinition(schedule, actionDefs.get(1), changes); + + return null; + } + }); + + // make sure the disposition lifecycle asOf date has NOT been updated as the period was + // changed for a step other than the current one + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @SuppressWarnings("deprecation") + public Object execute() throws Throwable + { + assertTrue(nodeService.hasAspect(record, ASPECT_DISPOSITION_LIFECYCLE)); + NodeRef ndNodeRef = nodeService.getChildAssocs(record, ASSOC_NEXT_DISPOSITION_ACTION, + RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + assertNotNull(ndNodeRef); + + assertNotNull(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF)); + Date asOfDate = (Date)nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF); + System.out.println("Disposition as of: " + asOfDate); + + // make sure the as of date is a month in the future + Calendar calendar = Calendar.getInstance(); + calendar.add(Calendar.MONTH, 3); + assertEquals("Expecting the asOf date to be unchanged", asOfDate.getMonth(), calendar.get(Calendar.MONTH)); + + // Check for the search properties having been populated + checkSearchAspect(record); + + return null; + } + }); + + // change the disposition schedule to be event based rather than time based i.e. + // remove the period properties and supply 2 events in its place. + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // define changes for schedule + Map changes = new HashMap(); + changes.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, null); + List events = new ArrayList(2); + events.add("no_longer_needed"); + events.add("case_complete"); + changes.put(RecordsManagementModel.PROP_DISPOSITION_EVENT, (Serializable)events); + + // update the first dispostion action definition + DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); + assertNotNull(schedule); + List actionDefs = schedule.getDispositionActionDefinitions(); + assertEquals(2, actionDefs.size()); + System.out.println("Removing period and adding no_longer_needed and case_complete to 1st step: " + + actionDefs.get(0).getName()); + updateDispositionActionDefinition(schedule, actionDefs.get(0), changes); + + return null; + } + }); + + // make sure the disposition lifecycle asOf date has been reset and there are now + // events hanging off the nextdispositionaction node + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + assertTrue(nodeService.hasAspect(record, ASPECT_DISPOSITION_LIFECYCLE)); + NodeRef ndNodeRef = nodeService.getChildAssocs(record, ASSOC_NEXT_DISPOSITION_ACTION, + RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + assertNotNull(ndNodeRef); + + Date asOfDate = (Date)nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF); + System.out.println("New disposition as of: " + asOfDate); + assertNull("Expecting asOfDate to be null", asOfDate); + + // make sure the 2 events are present + List events = nodeService.getChildAssocs(ndNodeRef, ASSOC_EVENT_EXECUTIONS, + RegexQNamePattern.MATCH_ALL); + assertEquals(2, events.size()); + NodeRef event1 = events.get(0).getChildRef(); + assertEquals("no_longer_needed", nodeService.getProperty(event1, PROP_EVENT_EXECUTION_NAME)); + NodeRef event2 = events.get(1).getChildRef(); + assertEquals("case_complete", nodeService.getProperty(event2, PROP_EVENT_EXECUTION_NAME)); + + // Check for the search properties having been populated + checkSearchAspect(record, false); + + return null; + } + }); + + // remove one of the events just added + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // define changes for schedule + Map changes = new HashMap(); + List events = new ArrayList(2); + events.add("case_complete"); + changes.put(RecordsManagementModel.PROP_DISPOSITION_EVENT, (Serializable)events); + + // update the first dispostion action definition + DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); + assertNotNull(schedule); + List actionDefs = schedule.getDispositionActionDefinitions(); + assertEquals(2, actionDefs.size()); + System.out.println("Removing no_longer_needed event from 1st step: " + + actionDefs.get(0).getName()); + updateDispositionActionDefinition(schedule, actionDefs.get(0), changes); + + return null; + } + }); + + // make sure the disposition lifecycle asOf date is still null and ensure there is only one event + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + assertTrue(nodeService.hasAspect(record, ASPECT_DISPOSITION_LIFECYCLE)); + NodeRef ndNodeRef = nodeService.getChildAssocs(record, ASSOC_NEXT_DISPOSITION_ACTION, + RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + assertNotNull(ndNodeRef); + + Date asOfDate = (Date)nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF); + assertNull("Expecting asOfDate to be null", asOfDate); + + // make sure only 1 event is present + List events = nodeService.getChildAssocs(ndNodeRef, ASSOC_EVENT_EXECUTIONS, + RegexQNamePattern.MATCH_ALL); + assertEquals(1, events.size()); + NodeRef event = events.get(0).getChildRef(); + assertEquals("case_complete", nodeService.getProperty(event, PROP_EVENT_EXECUTION_NAME)); + + // Check for the search properties having been populated + checkSearchAspect(record, false); + + return null; + } + }); + + // change the action on the first step from 'cutoff' to 'retain' + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // define changes for schedule + Map changes = new HashMap(); + changes.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, "retain"); + + // update the first dispostion action definition + DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); + assertNotNull(schedule); + List actionDefs = schedule.getDispositionActionDefinitions(); + assertEquals(2, actionDefs.size()); + System.out.println("Changing action of 1st step from '" + + actionDefs.get(0).getName() + "' to 'retain'"); + updateDispositionActionDefinition(schedule, actionDefs.get(0), changes); + + return null; + } + }); + + // make sure the disposition lifecycle asOf date is still null, ensure there is still only one event + // and most importantly that the action name is now 'retain' + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + assertTrue(nodeService.hasAspect(record, ASPECT_DISPOSITION_LIFECYCLE)); + NodeRef ndNodeRef = nodeService.getChildAssocs(record, ASSOC_NEXT_DISPOSITION_ACTION, + RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + assertNotNull(ndNodeRef); + + Date asOfDate = (Date)nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF); + assertNull("Expecting asOfDate to be null", asOfDate); + + // make sure only 1 event is present + List events = nodeService.getChildAssocs(ndNodeRef, ASSOC_EVENT_EXECUTIONS, + RegexQNamePattern.MATCH_ALL); + assertEquals(1, events.size()); + NodeRef event = events.get(0).getChildRef(); + assertEquals("case_complete", nodeService.getProperty(event, PROP_EVENT_EXECUTION_NAME)); + + String actionName = (String)nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION); + assertEquals("retain", actionName); + + // Check for the search properties having been populated + checkSearchAspect(record, false); + + return null; + } + }); + } + + private void updateDispositionActionDefinition(DispositionSchedule schedule, DispositionActionDefinition actionDefinition, Map actionDefinitionParams) + { + NodeRef nodeRef = actionDefinition.getNodeRef(); + Map before = nodeService.getProperties(nodeRef); + nodeService.addProperties(nodeRef, actionDefinitionParams); + Map after = nodeService.getProperties(nodeRef); + List updatedProps = determineChangedProps(before, after); + + refreshDispositionActionDefinition(nodeRef, updatedProps); + } + + private void refreshDispositionActionDefinition(NodeRef nodeRef, List updatedProps) + { + if (updatedProps != null) + { + Map params = new HashMap(); + params.put(BroadcastDispositionActionDefinitionUpdateAction.CHANGED_PROPERTIES, (Serializable)updatedProps); + rmActionService.executeRecordsManagementAction(nodeRef, BroadcastDispositionActionDefinitionUpdateAction.NAME, params); + } + + // Remove the unpublished update aspect + nodeService.removeAspect(nodeRef, ASPECT_UNPUBLISHED_UPDATE); + } + + private List determineChangedProps(Map oldProps, Map newProps) + { + List result = new ArrayList(); + for (QName qn : oldProps.keySet()) + { + if (newProps.get(qn) == null || + newProps.get(qn).equals(oldProps.get(qn)) == false) + { + result.add(qn); + } + } + for (QName qn : newProps.keySet()) + { + if (oldProps.get(qn) == null) + { + result.add(qn); + } + } + + return result; + } + + /** + * Tests the re-scheduling of disposition lifecycles when steps from the schedule are deleted + * (when using folder level disposition) + */ + public void testDispositionLifecycle_0318_reschedule_deletion_folderlevel() throws Exception + { + final NodeRef recordSeries = TestUtilities.getRecordSeries(rmService, nodeService, "Reports"); + setComplete(); + endTransaction(); + + // create a category + final NodeRef recordCategory = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + assertNotNull(recordSeries); + assertEquals("Reports", nodeService.getProperty(recordSeries, ContentModel.PROP_NAME)); + + return createRecordCategoryNode(recordSeries); + } + }); + + // define the disposition schedule for the category with several steps + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + assertNotNull(recordCategory); + + // define properties for both steps + Map step1 = new HashMap(); + step1.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, "cutoff"); + step1.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, "Cutoff when no longer needed"); + step1.put(RecordsManagementModel.PROP_DISPOSITION_EVENT, "no_longer_needed"); + + Map step2 = new HashMap(); + step2.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, "transfer"); + step2.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, "Transfer after 1 month"); + step2.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "month|1"); + step2.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD_PROPERTY, PROP_DISPOSITION_AS_OF); + + Map step3 = new HashMap(); + step3.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, "destroy"); + step3.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, "Destroy after 1 year"); + step3.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "year|1"); + step3.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD_PROPERTY, PROP_DISPOSITION_AS_OF); + + // add the action definitions to the schedule + DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); + assertNotNull(schedule); + dispositionService.addDispositionActionDefinition(schedule, step1); + dispositionService.addDispositionActionDefinition(schedule, step2); + dispositionService.addDispositionActionDefinition(schedule, step3); + + return null; + } + }); + + // create first record folder + final NodeRef recordFolder1 = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + return createRecordFolder(recordCategory, "Folder1"); + } + }); + + // create second record folder + final NodeRef recordFolder2 = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + return createRecordFolder(recordCategory, "Folder2"); + } + }); + + // make sure the disposition lifecycle is present and correct + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + assertNotNull(recordFolder1); + assertNotNull(recordFolder2); + + assertTrue(nodeService.hasAspect(recordFolder1, ASPECT_DISPOSITION_LIFECYCLE)); + assertTrue(nodeService.hasAspect(recordFolder2, ASPECT_DISPOSITION_LIFECYCLE)); + NodeRef folder1NextAction = nodeService.getChildAssocs(recordFolder1, ASSOC_NEXT_DISPOSITION_ACTION, + RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + assertNotNull(folder1NextAction); + NodeRef folder2NextAction = nodeService.getChildAssocs(recordFolder2, ASSOC_NEXT_DISPOSITION_ACTION, + RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + assertNotNull(folder2NextAction); + + // make sure both folders are on the cutoff step + assertEquals("cutoff", nodeService.getProperty(folder1NextAction, PROP_DISPOSITION_ACTION)); + assertEquals("cutoff", nodeService.getProperty(folder2NextAction, PROP_DISPOSITION_ACTION)); + + // make sure both folders have 1 event + assertEquals(1, nodeService.getChildAssocs(folder1NextAction, ASSOC_EVENT_EXECUTIONS, + RegexQNamePattern.MATCH_ALL).size()); + assertEquals(1, nodeService.getChildAssocs(folder2NextAction, ASSOC_EVENT_EXECUTIONS, + RegexQNamePattern.MATCH_ALL).size()); + + // move folder 2 onto next step + Map params = new HashMap(3); + params.put(CompleteEventAction.PARAM_EVENT_NAME, "no_longer_needed"); + params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_AT, new Date()); + params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_BY, "gavinc"); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + rmActionService.executeRecordsManagementAction(recordFolder2, "completeEvent", params); + rmActionService.executeRecordsManagementAction(recordFolder2, "cutoff"); + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + + return null; + } + }); + + // check the second folder is at step 2 and then attempt to remove a step from the disposition schedule + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + NodeRef folder2NextAction = nodeService.getChildAssocs(recordFolder2, ASSOC_NEXT_DISPOSITION_ACTION, + RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + assertNotNull(folder2NextAction); + assertEquals("transfer", (String)nodeService.getProperty(folder2NextAction, PROP_DISPOSITION_ACTION)); + + // check there are 3 steps to the schedule + DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); + assertNotNull(schedule); + List actionDefs = schedule.getDispositionActionDefinitions(); + assertEquals(3, actionDefs.size()); + + // attempt to remove step 1 from the schedule + try + { + dispositionService.removeDispositionActionDefinition(schedule, actionDefs.get(0)); + fail("Expecting the step deletion to be unsuccessful as record folders are present"); + } + catch (AlfrescoRuntimeException are) + { + // expected as steps are present, deletion not allowed + } + + return null; + } + }); + + // remove both record folders + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // remove record folders + nodeService.removeChild(recordCategory, recordFolder1); + nodeService.removeChild(recordCategory, recordFolder2); + return null; + } + }); + + // try removing last schedule step + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // make sure there are 3 steps + DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); + assertNotNull(schedule); + List actionDefs = schedule.getDispositionActionDefinitions(); + assertEquals(3, actionDefs.size()); + + // remove last step, should be successful this time + dispositionService.removeDispositionActionDefinition(schedule, actionDefs.get(2)); + + // make sure there are now 2 steps + schedule = dispositionService.getDispositionSchedule(recordCategory); + actionDefs = schedule.getDispositionActionDefinitions(); + assertEquals(2, actionDefs.size()); + + return null; + } + }); + + // *** NOTE: The commented out code below is potential tests for the step deletion behaviour *** + // *** we also need to add tests for deleting the step in the process where records or *** + // *** folders are on the last step i.e. what state should they be in if the last step *** + // *** is removed? *** + + /* + // check the second folder is at step 2 and remove the first step from the schedule + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + NodeRef folder2NextAction = nodeService.getChildAssocs(recordFolder2, ASSOC_NEXT_DISPOSITION_ACTION, + RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + assertNotNull(folder2NextAction); + assertEquals("transfer", (String)nodeService.getProperty(folder2NextAction, PROP_DISPOSITION_ACTION)); + + // remove step 1 from the schedule + DispositionSchedule schedule = rmService.getDispositionSchedule(recordCategory); + assertNotNull(schedule); + List actionDefs = schedule.getDispositionActionDefinitions(); + assertEquals(3, actionDefs.size()); + System.out.println("Removing schedule step 1 named: " + actionDefs.get(0).getName()); + rmService.removeDispositionActionDefinition(schedule, actionDefs.get(0)); + + return null; + } + }); + + // make sure the next action for folder 1 has moved on and folder 2 is unchanged, then delete last step + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + NodeRef folder1NextAction = nodeService.getChildAssocs(recordFolder1, ASSOC_NEXT_DISPOSITION_ACTION, + RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + assertNotNull(folder1NextAction); + NodeRef folder2NextAction = nodeService.getChildAssocs(recordFolder2, ASSOC_NEXT_DISPOSITION_ACTION, + RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + assertNotNull(folder2NextAction); + + // make sure both folders are on the cutoff step + assertEquals("transfer", nodeService.getProperty(folder1NextAction, PROP_DISPOSITION_ACTION)); + assertEquals("transfer", nodeService.getProperty(folder2NextAction, PROP_DISPOSITION_ACTION)); + + // Check for the search properties having been populated + checkSearchAspect(folder1NextAction); + checkSearchAspect(folder2NextAction); + + // remove the step in the last position from the schedule + DispositionSchedule schedule = rmService.getDispositionSchedule(recordCategory); + assertNotNull(schedule); + List actionDefs = schedule.getDispositionActionDefinitions(); + assertEquals(2, actionDefs.size()); + System.out.println("Removing schedule last step named: " + actionDefs.get(1).getName()); + rmService.removeDispositionActionDefinition(schedule, actionDefs.get(1)); + + return null; + } + }); + + // check there were no changes, then remove the only remaining step + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + NodeRef folder1NextAction = nodeService.getChildAssocs(recordFolder1, ASSOC_NEXT_DISPOSITION_ACTION, + RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + assertNotNull(folder1NextAction); + assertEquals("transfer", (String)nodeService.getProperty(folder1NextAction, PROP_DISPOSITION_ACTION)); + + NodeRef folder2NextAction = nodeService.getChildAssocs(recordFolder2, ASSOC_NEXT_DISPOSITION_ACTION, + RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + assertNotNull(folder2NextAction); + assertEquals("transfer", (String)nodeService.getProperty(folder2NextAction, PROP_DISPOSITION_ACTION)); + + // remove last remaining step from the schedule + DispositionSchedule schedule = rmService.getDispositionSchedule(recordCategory); + assertNotNull(schedule); + List actionDefs = schedule.getDispositionActionDefinitions(); + assertEquals(1, actionDefs.size()); + System.out.println("Removing last remaining schedule step named: " + actionDefs.get(0).getName()); + rmService.removeDispositionActionDefinition(schedule, actionDefs.get(0)); + + return null; + } + }); + + // check there are no schedule steps left and that both folders no longer have the disposition lifecycle aspect, + // then add a new step + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + assertEquals(0, nodeService.getChildAssocs(recordFolder1, ASSOC_NEXT_DISPOSITION_ACTION, + RegexQNamePattern.MATCH_ALL).size()); + assertEquals(0, nodeService.getChildAssocs(recordFolder2, ASSOC_NEXT_DISPOSITION_ACTION, + RegexQNamePattern.MATCH_ALL).size()); + assertFalse(nodeService.hasAspect(recordFolder1, ASPECT_DISPOSITION_LIFECYCLE)); + assertFalse(nodeService.hasAspect(recordFolder2, ASPECT_DISPOSITION_LIFECYCLE)); + + // ensure schedule is empty + DispositionSchedule schedule = rmService.getDispositionSchedule(recordCategory); + assertNotNull(schedule); + List actionDefs = schedule.getDispositionActionDefinitions(); + assertEquals(0, actionDefs.size()); + + // add a new step + Map step1 = new HashMap(); + step1.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, "retain"); + step1.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, "Retain for 25 years"); + step1.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "year|25"); + rmService.addDispositionActionDefinition(schedule, step1); + + return null; + } + }); + + // check both folders now have the retain action + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + NodeRef folder1NextAction = nodeService.getChildAssocs(recordFolder1, ASSOC_NEXT_DISPOSITION_ACTION, + RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + assertNotNull(folder1NextAction); + assertEquals("retain", (String)nodeService.getProperty(folder1NextAction, PROP_DISPOSITION_ACTION)); + assertNotNull(nodeService.getProperty(folder1NextAction, PROP_DISPOSITION_AS_OF)); + + NodeRef folder2NextAction = nodeService.getChildAssocs(recordFolder2, ASSOC_NEXT_DISPOSITION_ACTION, + RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + assertNotNull(folder2NextAction); + assertEquals("retain", (String)nodeService.getProperty(folder2NextAction, PROP_DISPOSITION_ACTION)); + assertNotNull(nodeService.getProperty(folder2NextAction, PROP_DISPOSITION_AS_OF)); + + return null; + } + }); + */ + } + + /** + * Tests the re-scheduling of disposition lifecycles when steps from the schedule are deleted + * (when using record level disposition) + */ + public void testDispositionLifecycle_0318_reschedule_deletion_recordlevel() throws Exception + { + final NodeRef recordSeries = TestUtilities.getRecordSeries(rmService, nodeService, "Reports"); + setComplete(); + endTransaction(); + + // create a category + final NodeRef recordCategory = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + assertNotNull(recordSeries); + assertEquals("Reports", nodeService.getProperty(recordSeries, ContentModel.PROP_NAME)); + + return createRecordCategoryNode(recordSeries); + } + }); + + // define the disposition schedule for the category with several steps + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + assertNotNull(recordCategory); + + // get the disposition schedule and turn on record level disposition + DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); + assertNotNull(schedule); + nodeService.setProperty(schedule.getNodeRef(), PROP_RECORD_LEVEL_DISPOSITION, true); + + // define properties for both steps + Map step1 = new HashMap(); + step1.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, "cutoff"); + step1.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, "Cutoff when no longer needed"); + step1.put(RecordsManagementModel.PROP_DISPOSITION_EVENT, "no_longer_needed"); + + Map step2 = new HashMap(); + step2.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, "transfer"); + step2.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, "Transfer after 1 month"); + step2.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "month|1"); + step2.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD_PROPERTY, PROP_DISPOSITION_AS_OF); + + Map step3 = new HashMap(); + step3.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, "destroy"); + step3.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, "Destroy after 1 year"); + step3.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "year|1"); + step3.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD_PROPERTY, PROP_DISPOSITION_AS_OF); + + // add the action definitions to the schedule + dispositionService.addDispositionActionDefinition(schedule, step1); + dispositionService.addDispositionActionDefinition(schedule, step2); + dispositionService.addDispositionActionDefinition(schedule, step3); + + return null; + } + }); + + // create first record folder + final NodeRef recordFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + return createRecordFolder(recordCategory, "Record Folder"); + } + }); + + // create a record + final NodeRef record = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + assertNotNull(recordFolder); + + // Create the document + Map props = new HashMap(1); + props.put(ContentModel.PROP_NAME, "MyRecord.txt"); + NodeRef record = nodeService.createNode(recordFolder, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "MyRecord.txt"), + ContentModel.TYPE_CONTENT).getChildRef(); + + // Set the content + ContentWriter writer = contentService.getWriter(record, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent("There is some content in this record"); + + return record; + } + }); + + // make sure the disposition lifecycle is present and correct + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + assertNotNull(record); + + assertTrue(nodeService.hasAspect(record, ASPECT_DISPOSITION_LIFECYCLE)); + NodeRef recordNextAction = nodeService.getChildAssocs(record, ASSOC_NEXT_DISPOSITION_ACTION, + RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + assertNotNull(recordNextAction); + + // make sure the record is on the cutoff step + assertEquals("cutoff", nodeService.getProperty(recordNextAction, PROP_DISPOSITION_ACTION)); + + // make sure the record has 1 event + assertEquals(1, nodeService.getChildAssocs(recordNextAction, ASSOC_EVENT_EXECUTIONS, + RegexQNamePattern.MATCH_ALL).size()); + + return null; + } + }); + + // check for steps in schedule then attempt to delete one + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // check there are 3 steps to the schedule + DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); + assertNotNull(schedule); + List actionDefs = schedule.getDispositionActionDefinitions(); + assertEquals(3, actionDefs.size()); + + // attempt to remove step 1 from the schedule + try + { + dispositionService.removeDispositionActionDefinition(schedule, actionDefs.get(0)); + fail("Expecting the step deletion to be unsuccessful as records are present"); + } + catch (AlfrescoRuntimeException are) + { + // expected as steps are present, deletion not allowed + } + + return null; + } + }); + + // remove the record (the folder can stay) + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // remove record folders + nodeService.removeChild(recordFolder, record); + return null; + } + }); + + // try removing last schedule step + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // make sure there are 3 steps + DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); + assertNotNull(schedule); + List actionDefs = schedule.getDispositionActionDefinitions(); + assertEquals(3, actionDefs.size()); + + // remove last step, should be successful this time + dispositionService.removeDispositionActionDefinition(schedule, actionDefs.get(2)); + + // make sure there are now 2 steps + schedule = dispositionService.getDispositionSchedule(recordCategory); + actionDefs = schedule.getDispositionActionDefinitions(); + assertEquals(2, actionDefs.size()); + + return null; + } + }); + } + + /** + * test a dispostion schedule being setup after a record folder and record + */ + public void testDispositionLifecycle_0318_existingfolders() throws Exception + { + final NodeRef recordSeries = TestUtilities.getRecordSeries(rmService, nodeService, "Reports"); + setComplete(); + endTransaction(); + + // create a category + final NodeRef recordCategory = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + assertNotNull(recordSeries); + assertEquals("Reports", nodeService.getProperty(recordSeries, ContentModel.PROP_NAME)); + + return createRecordCategoryNode(recordSeries); + } + }); + + // create a record folder + final NodeRef recordFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + assertNotNull(recordCategory); + return createRecordFolder(recordCategory, "Folder1"); + } + }); + + // define the disposition schedule for the category (Cut off monthly, hold 1 month, then destroy) + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + assertNotNull(recordFolder); + + // define properties for both steps + Map step1 = new HashMap(); + step1.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, "cutoff"); + step1.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, "Cutoff after 1 month"); + step1.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "month|1"); + + Map step2 = new HashMap(); + step2.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, "destroy"); + step2.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, "Destroy after 1 month"); + step2.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "month|1"); + step2.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD_PROPERTY, PROP_CUT_OFF_DATE); + + // add the action definitions to the schedule + DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); + assertNotNull(schedule); + + NodeRef temp = dispositionService.addDispositionActionDefinition(schedule, step1).getNodeRef(); + List updatedProps = new ArrayList(step1.keySet()); + refreshDispositionActionDefinition(temp, updatedProps); + + temp = dispositionService.addDispositionActionDefinition(schedule, step2).getNodeRef(); + updatedProps = new ArrayList(step2.keySet()); + refreshDispositionActionDefinition(temp, updatedProps); + + return null; + } + }); + + // make sure the disposition lifecycle is present and correct + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @SuppressWarnings("deprecation") + public Object execute() throws Throwable + { + assertNotNull(recordFolder); + + DispositionAction da = dispositionService.getNextDispositionAction(recordFolder); + assertNotNull(da); + + assertNotNull(da.getDispositionActionDefinition()); + assertNotNull(da.getDispositionActionDefinition().getId()); + assertEquals("cutoff", da.getName()); + Date asOfDate = da.getAsOfDate(); + assertNotNull(asOfDate); + + // make sure the as of date is a month in the future + Calendar cal = Calendar.getInstance(); + cal.add(Calendar.MONTH, 1); + int monthThen = cal.get(Calendar.MONTH); + assertEquals(asOfDate.getMonth(), monthThen); + + // make sure there aren't any events + assertEquals(0, da.getEventCompletionDetails().size()); + + // Check for the search properties having been populated + checkSearchAspect(recordFolder); + + return null; + } + }); + } + + /** + * Test the updating of a disposition schedule using folder level disposition + */ + public void testFolderLevelDispositionScheduleUpdate() throws Exception + { + final NodeRef recordSeries = TestUtilities.getRecordSeries(rmService, nodeService, "Reports"); + setComplete(); + endTransaction(); + + // create a category + final NodeRef recordCategory = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + assertNotNull(recordSeries); + assertEquals("Reports", nodeService.getProperty(recordSeries, ContentModel.PROP_NAME)); + + return createRecordCategoryNode(recordSeries); + } + }); + + // define the disposition schedule for the category (Cut off monthly, hold 1 month, then destroy) + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + assertNotNull(recordCategory); + + // get the disposition schedule and turn on record level disposition + DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); + assertNotNull(schedule); + nodeService.setProperty(schedule.getNodeRef(), PROP_DISPOSITION_INSTRUCTIONS, "Cutoff after 1 month then destroy after 1 month"); + nodeService.setProperty(schedule.getNodeRef(), PROP_DISPOSITION_AUTHORITY, "Alfresco"); + + return null; + } + }); + + // create a record folder + final NodeRef recordFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + return createRecordFolder(recordCategory, "Folder1"); + } + }); + + // check the created folder has the correctly populated search aspect, then update the schedule + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // check the folder has the search aspect + assertNotNull(recordFolder); + checkSearchAspect(recordFolder, false); + + // update the disposition schedule + DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); + assertNotNull(schedule); + nodeService.setProperty(schedule.getNodeRef(), PROP_DISPOSITION_INSTRUCTIONS, "Cutoff immediately when case is closed then destroy after 1 year"); + nodeService.setProperty(schedule.getNodeRef(), PROP_DISPOSITION_AUTHORITY, "DoD"); + + return null; + } + }); + + // check the search aspect has been kept in sync + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // check the folder has the search aspect + checkSearchAspect(recordFolder, false); + + return null; + } + }); + } + + /** + * Test the updating of a disposition schedule using record level disposition + */ + public void testRecordLevelDispositionScheduleUpdate() throws Exception + { + final NodeRef recordSeries = TestUtilities.getRecordSeries(rmService, nodeService, "Reports"); + setComplete(); + endTransaction(); + + // create a category + final NodeRef recordCategory = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + assertNotNull(recordSeries); + assertEquals("Reports", nodeService.getProperty(recordSeries, ContentModel.PROP_NAME)); + + return createRecordCategoryNode(recordSeries); + } + }); + + // define the disposition schedule for the category (Cut off monthly, hold 1 month, then destroy) + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + assertNotNull(recordCategory); + + // get the disposition schedule and turn on record level disposition + DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); + assertNotNull(schedule); + nodeService.setProperty(schedule.getNodeRef(), PROP_DISPOSITION_INSTRUCTIONS, "Cutoff after 1 month then destroy after 1 month"); + nodeService.setProperty(schedule.getNodeRef(), PROP_DISPOSITION_AUTHORITY, "Alfresco"); + nodeService.setProperty(schedule.getNodeRef(), PROP_RECORD_LEVEL_DISPOSITION, true); + + return null; + } + }); + + // create a record folder + final NodeRef recordFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + return createRecordFolder(recordCategory, "Folder1"); + } + }); + + // create a record + final NodeRef record = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + assertNotNull(recordFolder); + + // Create the document + Map props = new HashMap(1); + props.put(ContentModel.PROP_NAME, "MyRecord.txt"); + NodeRef record = nodeService.createNode(recordFolder, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "MyRecord.txt"), + ContentModel.TYPE_CONTENT).getChildRef(); + + // Set the content + ContentWriter writer = contentService.getWriter(record, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent("There is some content in this record"); + + return record; + } + }); + + // check the created folder has the correctly populated search aspect, then update the schedule + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // check the record has the search aspect + assertNotNull(record); + checkSearchAspect(record, false); + + // update the disposition schedule + DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); + assertNotNull(schedule); + nodeService.setProperty(schedule.getNodeRef(), PROP_DISPOSITION_INSTRUCTIONS, "Cutoff immediately when case is closed then destroy after 1 year"); + nodeService.setProperty(schedule.getNodeRef(), PROP_DISPOSITION_AUTHORITY, "DoD"); + + return null; + } + }); + + // check the search aspect has been kept in sync + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // check the record has the search aspect + checkSearchAspect(record, false); + + return null; + } + }); + } + + public void testUnCutoff() + { + final NodeRef recordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Reports", "AIS Audit Records"); + setComplete(); + endTransaction(); + + final NodeRef recordFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + + assertNotNull(recordCategory); + assertEquals("AIS Audit Records", nodeService.getProperty(recordCategory, ContentModel.PROP_NAME)); + + return createRecordFolder(recordCategory, "March AIS Audit Records"); + } + }); + + final NodeRef recordOne = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + return createRecord(recordFolder); + } + }); + + final Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + TestUtilities.declareRecord(recordOne, unprotectedNodeService, rmActionService); + + // Clock the asOf date back to ensure eligibility + NodeRef ndNodeRef = nodeService.getChildAssocs(recordFolder, ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + Date nowDate = calendar.getTime(); + assertFalse(nowDate.equals(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF))); + Map params = new HashMap(1); + params.put(EditDispositionActionAsOfDateAction.PARAM_AS_OF_DATE, nowDate); + rmActionService.executeRecordsManagementAction(recordFolder, "editDispositionActionAsOfDate", params); + assertTrue(nowDate.equals(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF))); + + // Cut off + rmActionService.executeRecordsManagementAction(recordFolder, "cutoff", null); + + // Check that everything appears to be cutoff + assertTrue(nodeService.hasAspect(recordFolder, ASPECT_CUT_OFF)); + List records = rmService.getRecords(recordFolder); + for (NodeRef record : records) + { + assertTrue(nodeService.hasAspect(record, ASPECT_CUT_OFF)); + } + DispositionAction da = dispositionService.getNextDispositionAction(recordFolder); + assertNotNull(da); + assertFalse("cutoff".equals(da.getName())); + checkLastDispositionAction(recordFolder, "cutoff", 1); + + // Revert the cutoff + rmActionService.executeRecordsManagementAction(recordFolder, "unCutoff", null); + + // Check that everything has been reverted + assertFalse(nodeService.hasAspect(recordFolder, ASPECT_CUT_OFF)); + records = rmService.getRecords(recordFolder); + for (NodeRef record : records) + { + assertFalse(nodeService.hasAspect(record, ASPECT_CUT_OFF)); + } + da = dispositionService.getNextDispositionAction(recordFolder); + assertNotNull(da); + assertTrue("cutoff".equals(da.getName())); + assertNull(da.getStartedAt()); + assertNull(da.getStartedBy()); + assertNull(da.getCompletedAt()); + assertNull(da.getCompletedBy()); + List history = dispositionService.getCompletedDispositionActions(recordFolder); + assertNotNull(history); + assertEquals(0, history.size()); + + return null; + } + }); + + } + + private void checkLastDispositionAction(NodeRef nodeRef, String daName, int expectedCount) + { + // Check the previous action details + List history = dispositionService.getCompletedDispositionActions(nodeRef); + assertNotNull(history); + assertEquals(expectedCount, history.size()); + DispositionAction lastDA = history.get(history.size()-1); + assertEquals(daName, lastDA.getName()); + assertNotNull(lastDA.getStartedAt()); + assertNotNull(lastDA.getStartedBy()); + assertNotNull(lastDA.getCompletedAt()); + assertNotNull(lastDA.getCompletedBy()); + // Check the "get last" method + lastDA = dispositionService.getLastCompletedDispostionAction(nodeRef); + assertEquals(daName, lastDA.getName()); + } + + public void testFreeze() throws Exception + { + final NodeRef recordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Reports", "AIS Audit Records"); + assertNotNull(recordCategory); + assertEquals("AIS Audit Records", this.nodeService.getProperty(recordCategory, ContentModel.PROP_NAME)); + + // Before we start just remove any outstanding holds + final NodeRef rootNode = this.rmService.getFilePlan(recordCategory); + List tempAssocs = this.nodeService.getChildAssocs(rootNode, ASSOC_HOLDS, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef tempAssoc : tempAssocs) + { + this.nodeService.deleteNode(tempAssoc.getChildRef()); + } + + final NodeRef recordFolder = createRecordFolder(recordCategory, "March AIS Audit Records"); + + setComplete(); + endTransaction(); + + final NodeRef recordOne = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + return createRecord(recordFolder, "one.txt"); + } + }); + final NodeRef recordTwo = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + return createRecord(recordFolder, "two.txt"); + } + }); + final NodeRef recordThree = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + return createRecord(recordFolder, "three.txt"); + } + }); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + assertTrue(nodeService.hasAspect(recordOne, ASPECT_RECORD)); + assertTrue(nodeService.hasAspect(recordOne, ASPECT_FILE_PLAN_COMPONENT)); + + // Freeze the record + Map params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "reason1"); + rmActionService.executeRecordsManagementAction(recordOne, "freeze", params); + + return null; + } + }); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // Check the hold exists + List holdAssocs = nodeService.getChildAssocs(rootNode, ASSOC_HOLDS, RegexQNamePattern.MATCH_ALL); + assertNotNull(holdAssocs); + assertEquals(1, holdAssocs.size()); + NodeRef holdNodeRef = holdAssocs.get(0).getChildRef(); + assertEquals("reason1", nodeService.getProperty(holdNodeRef, PROP_HOLD_REASON)); + List freezeAssocs = nodeService.getChildAssocs(holdNodeRef); + assertNotNull(freezeAssocs); + assertEquals(1, freezeAssocs.size()); + + // Check the nodes are frozen + assertTrue(nodeService.hasAspect(recordOne, ASPECT_FROZEN)); + assertNotNull(nodeService.getProperty(recordOne, PROP_FROZEN_AT)); + assertNotNull(nodeService.getProperty(recordOne, PROP_FROZEN_BY)); + assertFalse(nodeService.hasAspect(recordTwo, ASPECT_FROZEN)); + assertFalse(nodeService.hasAspect(recordThree, ASPECT_FROZEN)); + + // check the records have the hold reason reflected on the search aspect + assertEquals("reason1", nodeService.getProperty(recordOne, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); + assertNull(nodeService.getProperty(recordTwo, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); + assertNull(nodeService.getProperty(recordThree, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); + + // Update the freeze reason + Map params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "reason1changed"); + rmActionService.executeRecordsManagementAction(holdNodeRef, "editHoldReason", params); + + // Check the hold has been updated + String updatedHoldReason = (String)nodeService.getProperty(holdNodeRef, PROP_HOLD_REASON); + assertEquals("reason1changed", updatedHoldReason); + + return null; + } + }); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // check the search fields on the records have also been updated + assertEquals("reason1changed", nodeService.getProperty(recordOne, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); + assertNull(nodeService.getProperty(recordTwo, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); + assertNull(nodeService.getProperty(recordThree, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); + + // Freeze a number of records + Map params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "reason2"); + List records = new ArrayList(2); + records.add(recordOne); + records.add(recordTwo); + records.add(recordThree); + rmActionService.executeRecordsManagementAction(records, "freeze", params); + + return null; + } + }); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // Check the holds exist + List holdAssocs = nodeService.getChildAssocs(rootNode, ASSOC_HOLDS, RegexQNamePattern.MATCH_ALL); + assertNotNull(holdAssocs); + assertEquals(2, holdAssocs.size()); + for (ChildAssociationRef holdAssoc : holdAssocs) + { + String reason = (String)nodeService.getProperty(holdAssoc.getChildRef(), PROP_HOLD_REASON); + if (reason.equals("reason2") == true) + { + List freezeAssocs = nodeService.getChildAssocs(holdAssoc.getChildRef()); + assertNotNull(freezeAssocs); + assertEquals(3, freezeAssocs.size()); + } + else if (reason.equals("reason1changed") == true) + { + List freezeAssocs = nodeService.getChildAssocs(holdAssoc.getChildRef()); + assertNotNull(freezeAssocs); + assertEquals(1, freezeAssocs.size()); + } + } + + // Check the nodes are frozen + final List testRecords = Arrays.asList(new NodeRef[]{recordOne, recordTwo, recordThree}); + for (NodeRef nr : testRecords) + { + assertTrue(nodeService.hasAspect(nr, ASPECT_FROZEN)); + assertNotNull(nodeService.getProperty(nr, PROP_FROZEN_AT)); + assertNotNull(nodeService.getProperty(nr, PROP_FROZEN_BY)); + assertNotNull(nodeService.getProperty(nr, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); + } + + // Unfreeze a node + rmActionService.executeRecordsManagementAction(recordThree, "unfreeze"); + + return null; + } + }); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // Check the holds + List holdAssocs = nodeService.getChildAssocs(rootNode, ASSOC_HOLDS, RegexQNamePattern.MATCH_ALL); + assertNotNull(holdAssocs); + assertEquals(2, holdAssocs.size()); + for (ChildAssociationRef holdAssoc : holdAssocs) + { + String reason = (String)nodeService.getProperty(holdAssoc.getChildRef(), PROP_HOLD_REASON); + if (reason.equals("reason2") == true) + { + List freezeAssocs = nodeService.getChildAssocs(holdAssoc.getChildRef()); + assertNotNull(freezeAssocs); + assertEquals(2, freezeAssocs.size()); + } + else if (reason.equals("reason1changed") == true) + { + List freezeAssocs = nodeService.getChildAssocs(holdAssoc.getChildRef()); + assertNotNull(freezeAssocs); + assertEquals(1, freezeAssocs.size()); + } + } + + // Check the nodes are frozen + assertTrue(nodeService.hasAspect(recordOne, ASPECT_FROZEN)); + assertNotNull(nodeService.getProperty(recordOne, PROP_FROZEN_AT)); + assertNotNull(nodeService.getProperty(recordOne, PROP_FROZEN_BY)); + assertEquals("reason2", nodeService.getProperty(recordOne, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); + assertTrue(nodeService.hasAspect(recordTwo, ASPECT_FROZEN)); + assertNotNull(nodeService.getProperty(recordTwo, PROP_FROZEN_AT)); + assertNotNull(nodeService.getProperty(recordTwo, PROP_FROZEN_BY)); + assertEquals("reason2", nodeService.getProperty(recordTwo, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); + assertFalse(nodeService.hasAspect(recordThree, ASPECT_FROZEN)); + assertNull(nodeService.getProperty(recordThree, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); + + return null; + } + }); + + // Put the relinquish hold request into its own transaction + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // Check the holds + List holdAssocs = nodeService.getChildAssocs(rootNode, ASSOC_HOLDS, RegexQNamePattern.MATCH_ALL); + assertNotNull(holdAssocs); + assertEquals(2, holdAssocs.size()); + // Relinquish the first hold + NodeRef holdNodeRef = holdAssocs.get(0).getChildRef(); + assertEquals("reason1changed", nodeService.getProperty(holdNodeRef, PROP_HOLD_REASON)); + + rmActionService.executeRecordsManagementAction(holdNodeRef, "relinquishHold"); + + // Check the holds + holdAssocs = nodeService.getChildAssocs(rootNode, ASSOC_HOLDS, RegexQNamePattern.MATCH_ALL); + assertNotNull(holdAssocs); + assertEquals(1, holdAssocs.size()); + holdNodeRef = holdAssocs.get(0).getChildRef(); + assertEquals("reason2", nodeService.getProperty(holdNodeRef, PROP_HOLD_REASON)); + List freezeAssocs = nodeService.getChildAssocs(holdNodeRef); + assertNotNull(freezeAssocs); + assertEquals(2, freezeAssocs.size()); + + return null; + } + }); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // Check the nodes are frozen + assertTrue(nodeService.hasAspect(recordOne, ASPECT_FROZEN)); + assertNotNull(nodeService.getProperty(recordOne, PROP_FROZEN_AT)); + assertNotNull(nodeService.getProperty(recordOne, PROP_FROZEN_BY)); + // TODO: record one is still linked to a hold so should have the original hold reason + // on the search aspect but we're presuming just one hold for now so the search hold + // reason will remain unchanged + assertEquals("reason2", nodeService.getProperty(recordOne, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); + assertTrue(nodeService.hasAspect(recordTwo, ASPECT_FROZEN)); + assertNotNull(nodeService.getProperty(recordTwo, PROP_FROZEN_AT)); + assertNotNull(nodeService.getProperty(recordTwo, PROP_FROZEN_BY)); + assertEquals("reason2", nodeService.getProperty(recordTwo, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); + assertFalse(nodeService.hasAspect(recordThree, ASPECT_FROZEN)); + assertNull(nodeService.getProperty(recordThree, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); + + // Unfreeze + rmActionService.executeRecordsManagementAction(recordOne, "unfreeze"); + + return null; + } + }); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // Check the holds + List holdAssocs = nodeService.getChildAssocs(rootNode, ASSOC_HOLDS, RegexQNamePattern.MATCH_ALL); + assertNotNull(holdAssocs); + assertEquals(1, holdAssocs.size()); + NodeRef holdNodeRef = holdAssocs.get(0).getChildRef(); + assertEquals("reason2", nodeService.getProperty(holdNodeRef, PROP_HOLD_REASON)); + List freezeAssocs = nodeService.getChildAssocs(holdNodeRef); + assertNotNull(freezeAssocs); + assertEquals(1, freezeAssocs.size()); + + // Check the nodes are frozen + assertFalse(nodeService.hasAspect(recordOne, ASPECT_FROZEN)); + assertNull(nodeService.getProperty(recordOne, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); + assertTrue(nodeService.hasAspect(recordTwo, ASPECT_FROZEN)); + assertNotNull(nodeService.getProperty(recordTwo, PROP_FROZEN_AT)); + assertNotNull(nodeService.getProperty(recordTwo, PROP_FROZEN_BY)); + assertEquals("reason2", nodeService.getProperty(recordTwo, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); + assertFalse(nodeService.hasAspect(recordThree, ASPECT_FROZEN)); + assertNull(nodeService.getProperty(recordThree, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); + + // Unfreeze + rmActionService.executeRecordsManagementAction(recordTwo, "unfreeze"); + + return null; + } + }); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // Check the holds + List holdAssocs = nodeService.getChildAssocs(rootNode, ASSOC_HOLDS, RegexQNamePattern.MATCH_ALL); + assertNotNull(holdAssocs); + assertEquals(0, holdAssocs.size()); + + // Check the nodes are unfrozen + assertFalse(nodeService.hasAspect(recordOne, ASPECT_FROZEN)); + assertFalse(nodeService.hasAspect(recordTwo, ASPECT_FROZEN)); + assertFalse(nodeService.hasAspect(recordThree, ASPECT_FROZEN)); + + // check the search hold reason is null on all records + assertNull(nodeService.getProperty(recordOne, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); + assertNull(nodeService.getProperty(recordTwo, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); + assertNull(nodeService.getProperty(recordThree, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); + + return null; + } + }); + } + + public void testAutoSuperseded() + { + final NodeRef recordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Civilian Files", "Employee Performance File System Records"); + assertNotNull(recordCategory); + assertEquals("Employee Performance File System Records", this.nodeService.getProperty(recordCategory, ContentModel.PROP_NAME)); + + final NodeRef recordFolder = createRecordFolder(recordCategory, "Test Record Folder"); + + // Before we start just remove any outstanding transfers + final NodeRef rootNode = this.rmService.getFilePlan(recordCategory); + List tempAssocs = this.nodeService.getChildAssocs(rootNode, ASSOC_TRANSFERS, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef tempAssoc : tempAssocs) + { + this.nodeService.deleteNode(tempAssoc.getChildRef()); + } + + setComplete(); + endTransaction(); + + final NodeRef recordOne = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + return createRecord(recordFolder, "one.txt"); + } + }); + final NodeRef recordTwo = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + return createRecord(recordFolder, "two.txt"); + } + }); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + assertTrue(nodeService.hasAspect(recordOne, ASPECT_RECORD)); + + TestUtilities.declareRecord(recordOne, unprotectedNodeService, rmActionService); + TestUtilities.declareRecord(recordTwo, unprotectedNodeService, rmActionService); + + return null; + } + }); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + + DispositionAction da = dispositionService.getNextDispositionAction(recordTwo); + assertNotNull(da); + assertEquals("cutoff", da.getName()); + assertFalse(da.isEventsEligible()); + List events = da.getEventCompletionDetails(); + assertNotNull(events); + assertEquals(1, events.size()); + EventCompletionDetails event = events.get(0); + assertEquals("superseded", event.getEventName()); + assertFalse(event.isEventComplete()); + assertNull(event.getEventCompletedAt()); + assertNull(event.getEventCompletedBy()); + + rmAdminService.addCustomReference(recordOne, recordTwo, QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_URI, "supersedes")); + + return null; + } + }); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + DispositionAction da = dispositionService.getNextDispositionAction(recordTwo); + assertNotNull(da); + assertEquals("cutoff", da.getName()); + assertTrue(da.isEventsEligible()); + List events = da.getEventCompletionDetails(); + assertNotNull(events); + assertEquals(1, events.size()); + EventCompletionDetails event = events.get(0); + assertEquals("superseded", event.getEventName()); + assertTrue(event.isEventComplete()); + assertNotNull(event.getEventCompletedAt()); + assertNotNull(event.getEventCompletedBy()); + + return null; + } + }); + } + + public void testVersioned() + { + final NodeRef recordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Civilian Files", "Employee Performance File System Records"); + assertNotNull(recordCategory); + assertEquals("Employee Performance File System Records", this.nodeService.getProperty(recordCategory, ContentModel.PROP_NAME)); + + final NodeRef recordFolder = createRecordFolder(recordCategory, "Test Record Folder"); + + // Before we start just remove any outstanding transfers + final NodeRef rootNode = this.rmService.getFilePlan(recordCategory); + List tempAssocs = this.nodeService.getChildAssocs(rootNode, ASSOC_TRANSFERS, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef tempAssoc : tempAssocs) + { + this.nodeService.deleteNode(tempAssoc.getChildRef()); + } + + setComplete(); + endTransaction(); + + final NodeRef recordOne = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + return createRecord(recordFolder, "one.txt"); + } + }); + final NodeRef recordTwo = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + return createRecord(recordFolder, "two.txt"); + } + }); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + assertTrue(nodeService.hasAspect(recordOne, ASPECT_RECORD)); + + TestUtilities.declareRecord(recordOne, unprotectedNodeService, rmActionService); + TestUtilities.declareRecord(recordTwo, unprotectedNodeService, rmActionService); + + assertFalse(nodeService.hasAspect(recordOne, ASPECT_VERSIONED_RECORD)); + + rmAdminService.addCustomReference(recordOne, recordTwo, QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_URI, "versions")); + + return null; + } + }); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + assertTrue(nodeService.hasAspect(recordOne, ASPECT_VERSIONED_RECORD)); + + rmAdminService.removeCustomReference(recordOne, recordTwo, QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_URI, "versions")); + + return null; + } + }); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + assertFalse(nodeService.hasAspect(recordOne, ASPECT_VERSIONED_RECORD)); + + return null; + } + }); + } + + public void testDispositionLifecycle_0430_02_transfer() throws Exception + { + final NodeRef recordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Civilian Files", "Foreign Employee Award Files"); + assertNotNull(recordCategory); + assertEquals("Foreign Employee Award Files", this.nodeService.getProperty(recordCategory, ContentModel.PROP_NAME)); + + final NodeRef recordFolder = createRecordFolder(recordCategory, "Test Record Folder"); + + // Before we start just remove any outstanding transfers + final NodeRef rootNode = this.rmService.getFilePlan(recordCategory); + List tempAssocs = this.nodeService.getChildAssocs(rootNode, ASSOC_TRANSFERS, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef tempAssoc : tempAssocs) + { + this.nodeService.deleteNode(tempAssoc.getChildRef()); + } + + setComplete(); + endTransaction(); + + final NodeRef recordOne = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + return createRecord(recordFolder, "one.txt"); + } + }); + final NodeRef recordTwo = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + return createRecord(recordFolder, "two.txt"); + } + }); + final NodeRef recordThree = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + // Create the document + Map props = new HashMap(1); + props.put(ContentModel.PROP_NAME, "three.pdf"); + NodeRef recordThree = nodeService.createNode(recordFolder, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "three.pdf"), + ContentModel.TYPE_CONTENT, + props).getChildRef(); + + // Set the content + ContentWriter writer = contentService.getWriter(recordOne, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_PDF); + writer.setEncoding("UTF-8"); + writer.putContent("asdas"); + + return recordThree; + } + }); + + final DispositionAction da = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public DispositionAction execute() throws Throwable + { + // Declare the records + TestUtilities.declareRecord(recordOne, unprotectedNodeService, rmActionService); + TestUtilities.declareRecord(recordTwo, unprotectedNodeService, rmActionService); + TestUtilities.declareRecord(recordThree, unprotectedNodeService, rmActionService); + + // Cutoff + Map params = new HashMap(3); + params.put(CompleteEventAction.PARAM_EVENT_NAME, "case_complete"); + params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_AT, new Date()); + params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_BY, "roy"); + rmActionService.executeRecordsManagementAction(recordFolder, "completeEvent", params); + rmActionService.executeRecordsManagementAction(recordFolder, "cutoff"); + + checkLastDispositionAction(recordFolder, "cutoff", 1); + + DispositionAction da = dispositionService.getNextDispositionAction(recordFolder); + assertNotNull(da); + assertEquals("transfer", da.getName()); + + assertFalse(nodeService.hasAspect(recordFolder, ASPECT_TRANSFERRED)); + + return da; + } + }); + + // Do the transfer + final Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + + final Object actionResult = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // Clock the asOf date back to ensure eligibility + Date nowDate = calendar.getTime(); + assertFalse(nowDate.equals(nodeService.getProperty(da.getNodeRef(), PROP_DISPOSITION_AS_OF))); + Map params = new HashMap(1); + params.put(EditDispositionActionAsOfDateAction.PARAM_AS_OF_DATE, nowDate); + rmActionService.executeRecordsManagementAction(recordFolder, "editDispositionActionAsOfDate", params); + assertTrue(nowDate.equals(nodeService.getProperty(da.getNodeRef(), PROP_DISPOSITION_AS_OF))); + + return rmActionService.executeRecordsManagementAction(recordFolder, "transfer", null); + } + }); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + assertFalse(nodeService.hasAspect(recordFolder, ASPECT_TRANSFERRED)); + assertFalse(nodeService.hasAspect(recordOne, ASPECT_TRANSFERRED)); + assertFalse(nodeService.hasAspect(recordTwo, ASPECT_TRANSFERRED)); + assertFalse(nodeService.hasAspect(recordThree, ASPECT_TRANSFERRED)); + + // Check that the next disposition action is still in the correct state + DispositionAction da = dispositionService.getNextDispositionAction(recordFolder); + assertNotNull(da); + assertEquals("transfer", da.getName()); + assertNotNull(da.getStartedAt()); + assertNotNull(da.getStartedBy()); + assertNull(da.getCompletedAt()); + assertNull(da.getCompletedBy()); + + checkLastDispositionAction(recordFolder, "cutoff", 1); + + // Check that the transfer object is created + assertNotNull(rootNode); + List assocs = nodeService.getChildAssocs(rootNode, ASSOC_TRANSFERS, RegexQNamePattern.MATCH_ALL); + assertNotNull(assocs); + assertEquals(1, assocs.size()); + NodeRef transferNodeRef = assocs.get(0).getChildRef(); + assertEquals(TYPE_TRANSFER, nodeService.getType(transferNodeRef)); + assertTrue(((Boolean)nodeService.getProperty(transferNodeRef, PROP_TRANSFER_PDF_INDICATOR)).booleanValue()); + assertEquals("Offline Storage", (String)nodeService.getProperty(transferNodeRef, PROP_TRANSFER_LOCATION)); + assertNotNull(actionResult); + assertEquals(transferNodeRef, ((RecordsManagementActionResult)actionResult).getValue()); + List children = nodeService.getChildAssocs(transferNodeRef, ASSOC_TRANSFERRED, RegexQNamePattern.MATCH_ALL); + assertNotNull(children); + assertEquals(1, children.size()); + + + // Complete the transfer + rmActionService.executeRecordsManagementAction(assocs.get(0).getChildRef(), "transferComplete"); + + // Check nodes have been marked correctly + assertTrue(nodeService.hasAspect(recordFolder, ASPECT_TRANSFERRED)); + assertTrue(nodeService.hasAspect(recordOne, ASPECT_TRANSFERRED)); + assertTrue(nodeService.hasAspect(recordTwo, ASPECT_TRANSFERRED)); + assertTrue(nodeService.hasAspect(recordThree, ASPECT_TRANSFERRED)); + + // Check the transfer object is deleted + assocs = nodeService.getChildAssocs(rootNode, ASSOC_TRANSFERS, RegexQNamePattern.MATCH_ALL); + assertNotNull(assocs); + assertEquals(0, assocs.size()); + + // Check the disposition action has been moved on + da = dispositionService.getNextDispositionAction(recordFolder); + assertNotNull(da); + assertEquals("transfer", da.getName()); + assertNull(da.getStartedAt()); + assertNull(da.getStartedBy()); + assertNull(da.getCompletedAt()); + assertNull(da.getCompletedBy()); + assertFalse(dispositionService.isNextDispositionActionEligible(recordFolder)); + + checkLastDispositionAction(recordFolder, "transfer", 2); + + return null; + } + }); + } + + private void checkSearchAspect(NodeRef record) + { + checkSearchAspect(record, true); + } + + private void checkSearchAspect(NodeRef record, boolean isPeriodSet) + { + DispositionAction da = dispositionService.getNextDispositionAction(record); + if (da != null) + { + assertTrue(nodeService.hasAspect(record, RecordsManagementSearchBehaviour.ASPECT_RM_SEARCH)); + assertEquals(da.getName(), + nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_DISPOSITION_ACTION_NAME)); + assertEquals(da.getAsOfDate(), + nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_DISPOSITION_ACTION_AS_OF)); + assertEquals(nodeService.getProperty(da.getNodeRef(), PROP_DISPOSITION_EVENTS_ELIGIBLE), + nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_DISPOSITION_EVENTS_ELIGIBLE)); + + int eventCount = da.getEventCompletionDetails().size(); + Collection events = (Collection)nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_DISPOSITION_EVENTS); + if (eventCount == 0) + { + assertNull(events); + } + else + { + assertEquals(eventCount, events.size()); + } + + DispositionActionDefinition daDef = da.getDispositionActionDefinition(); + assertNotNull(daDef); + Period period = daDef.getPeriod(); + if (isPeriodSet) + { + assertNotNull(period); + assertEquals(period.getPeriodType(), nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_DISPOSITION_PERIOD)); + assertEquals(period.getExpression(), nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_DISPOSITION_PERIOD_EXPRESSION)); + } + else + { + assertNull(period); + assertNull(nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_DISPOSITION_PERIOD)); + assertNull(nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_DISPOSITION_PERIOD_EXPRESSION)); + } + } + + DispositionSchedule ds = dispositionService.getDispositionSchedule(record); + Boolean value = (Boolean)nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_HAS_DISPOITION_SCHEDULE); + String dsInstructions = (String)nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_DISPOITION_INSTRUCTIONS); + String dsAuthority = (String)nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_DISPOITION_AUTHORITY); + if (ds != null) + { + assertTrue(value); + assertEquals(ds.getDispositionInstructions(), dsInstructions); + assertEquals(ds.getDispositionAuthority(), dsAuthority); + } + else + { + assertFalse(value); + } + + VitalRecordDefinition vrd = vitalRecordService.getVitalRecordDefinition(record); + if (vrd == null) + { + assertNull(nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_VITAL_RECORD_REVIEW_PERIOD)); + assertNull(nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_VITAL_RECORD_REVIEW_PERIOD_EXPRESSION)); + } + else + { + assertEquals(vrd.getReviewPeriod().getPeriodType(), + nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_VITAL_RECORD_REVIEW_PERIOD)); + assertEquals(vrd.getReviewPeriod().getExpression(), + nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_VITAL_RECORD_REVIEW_PERIOD_EXPRESSION)); + } + } + + + public void testDispositionLifecycle_0430_01_recordleveldisposition() throws Exception + { + NodeRef recordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Civilian Files", "Employee Performance File System Records"); + assertNotNull(recordCategory); + assertEquals("Employee Performance File System Records", this.nodeService.getProperty(recordCategory, ContentModel.PROP_NAME)); + + NodeRef recordFolder = createRecordFolder(recordCategory, "My Record Folder"); + + setComplete(); + endTransaction(); + + UserTransaction txn = transactionService.getUserTransaction(false); + txn.begin(); + + NodeRef recordOne = createRecord(recordFolder, "one.txt"); + + txn.commit(); + txn = transactionService.getUserTransaction(false); + txn.begin(); + + TestUtilities.declareRecord(recordOne, unprotectedNodeService, rmActionService); + + // Check the disposition action + assertTrue(this.nodeService.hasAspect(recordOne, ASPECT_DISPOSITION_LIFECYCLE)); + assertFalse(this.nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE)); + + // Check the dispostion action + DispositionAction da = dispositionService.getNextDispositionAction(recordOne); + assertNotNull(da); + assertEquals("cutoff", da.getDispositionActionDefinition().getName()); + assertNull(da.getAsOfDate()); + assertFalse((Boolean)this.nodeService.getProperty(da.getNodeRef(), PROP_DISPOSITION_EVENTS_ELIGIBLE)); + assertEquals(true, da.getDispositionActionDefinition().eligibleOnFirstCompleteEvent()); + List events = da.getEventCompletionDetails(); + assertNotNull(events); + assertEquals(1, events.size()); + EventCompletionDetails event = events.get(0); + + Map params = new HashMap(3); + params.put(CompleteEventAction.PARAM_EVENT_NAME, event.getEventName()); + params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_AT, new Date()); + params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_BY, "roy"); + + this.rmActionService.executeRecordsManagementAction(recordOne, "completeEvent", params); + + txn.commit(); + txn = transactionService.getUserTransaction(false); + txn.begin(); + + assertTrue((Boolean)this.nodeService.getProperty(da.getNodeRef(), PROP_DISPOSITION_EVENTS_ELIGIBLE)); + + // Do the commit action + this.rmActionService.executeRecordsManagementAction(recordOne, "cutoff", null); + + txn.commit(); + txn = transactionService.getUserTransaction(false); + txn.begin(); + + // Check events are gone + da = dispositionService.getNextDispositionAction(recordOne); + + assertNotNull(da); + assertEquals("destroy", da.getDispositionActionDefinition().getName()); + assertNotNull(da.getAsOfDate()); + assertFalse((Boolean)this.nodeService.getProperty(da.getNodeRef(), PROP_DISPOSITION_EVENTS_ELIGIBLE)); + events = da.getEventCompletionDetails(); + assertNotNull(events); + assertEquals(0, events.size()); + + final Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + + // Clock the asOf date back to ensure eligibility for destruction + NodeRef ndNodeRef = nodeService.getChildAssocs(recordOne, ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + Date nowDate = calendar.getTime(); + assertFalse(nowDate.equals(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF))); + params.clear(); + params.put(EditDispositionActionAsOfDateAction.PARAM_AS_OF_DATE, nowDate); + rmActionService.executeRecordsManagementAction(recordOne, "editDispositionActionAsOfDate", params); + assertTrue(nowDate.equals(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF))); + + + assertNotNull(nodeService.getProperty(recordOne, ContentModel.PROP_CONTENT)); + + rmActionService.executeRecordsManagementAction(recordOne, "destroy", null); + + // Check that the node has been ghosted + assertTrue(nodeService.exists(recordOne)); + assertTrue(nodeService.hasAspect(recordOne, RecordsManagementModel.ASPECT_GHOSTED)); + assertNull(nodeService.getProperty(recordOne, ContentModel.PROP_CONTENT)); + + txn.commit(); + } + + public void testDispositionLifecycle_0412_03_eventtest() throws Exception + { + NodeRef recordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Military Files", "Personnel Security Program Records"); + assertNotNull(recordCategory); + assertEquals("Personnel Security Program Records", this.nodeService.getProperty(recordCategory, ContentModel.PROP_NAME)); + + Map folderProps = new HashMap(1); + folderProps.put(ContentModel.PROP_NAME, "My Folder"); + NodeRef recordFolder = this.nodeService.createNode(recordCategory, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "My Folder"), + TYPE_RECORD_FOLDER).getChildRef(); + setComplete(); + endTransaction(); + + UserTransaction txn = transactionService.getUserTransaction(false); + txn.begin(); + + NodeRef recordOne = createRecord(recordFolder); + + txn.commit(); + txn = transactionService.getUserTransaction(false); + txn.begin(); + + TestUtilities.declareRecord(recordOne, unprotectedNodeService, rmActionService); + + // NOTE the disposition is being managed at a folder level ... + + // Check the disposition action + assertFalse(this.nodeService.hasAspect(recordOne, ASPECT_DISPOSITION_LIFECYCLE)); + assertTrue(this.nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE)); + + // Check the dispostion action + DispositionAction da = dispositionService.getNextDispositionAction(recordFolder); + assertNotNull(da); + assertEquals("cutoff", da.getDispositionActionDefinition().getName()); + assertNull(da.getAsOfDate()); + assertFalse((Boolean)this.nodeService.getProperty(da.getNodeRef(), PROP_DISPOSITION_EVENTS_ELIGIBLE)); + assertEquals(false, da.getDispositionActionDefinition().eligibleOnFirstCompleteEvent()); + List events = da.getEventCompletionDetails(); + assertNotNull(events); + assertEquals(3, events.size()); + + checkSearchAspect(recordFolder); + + txn.commit(); + txn = transactionService.getUserTransaction(false); + txn.begin(); + + EventCompletionDetails ecd = events.get(0); + assertFalse(ecd.isEventComplete()); + assertNull(ecd.getEventCompletedBy()); + assertNull(ecd.getEventCompletedAt()); + + Map params = new HashMap(3); + params.put(CompleteEventAction.PARAM_EVENT_NAME, events.get(0).getEventName()); + params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_AT, new Date()); + params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_BY, "roy"); + + checkSearchAspect(recordFolder); + + this.rmActionService.executeRecordsManagementAction(recordFolder, "completeEvent", params); + + txn.commit(); + txn = transactionService.getUserTransaction(false); + txn.begin(); + + assertFalse((Boolean)this.nodeService.getProperty(da.getNodeRef(), PROP_DISPOSITION_EVENTS_ELIGIBLE)); + assertEquals(false, da.getDispositionActionDefinition().eligibleOnFirstCompleteEvent()); + events = da.getEventCompletionDetails(); + assertNotNull(events); + assertEquals(3, events.size()); + + params = new HashMap(3); + params.put(CompleteEventAction.PARAM_EVENT_NAME, events.get(1).getEventName()); + params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_AT, new Date()); + params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_BY, "roy"); + + checkSearchAspect(recordFolder); + + this.rmActionService.executeRecordsManagementAction(recordFolder, "completeEvent", params); + + txn.commit(); + txn = transactionService.getUserTransaction(false); + txn.begin(); + + assertFalse((Boolean)this.nodeService.getProperty(da.getNodeRef(), PROP_DISPOSITION_EVENTS_ELIGIBLE)); + + params = new HashMap(3); + params.put(CompleteEventAction.PARAM_EVENT_NAME, events.get(2).getEventName()); + params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_AT, new Date()); + params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_BY, "roy"); + + checkSearchAspect(recordFolder); + + this.rmActionService.executeRecordsManagementAction(recordFolder, "completeEvent", params); + + txn.commit(); + txn = transactionService.getUserTransaction(false); + txn.begin(); + + assertTrue((Boolean)this.nodeService.getProperty(da.getNodeRef(), PROP_DISPOSITION_EVENTS_ELIGIBLE)); + + events = da.getEventCompletionDetails(); + assertNotNull(events); + assertEquals(3, events.size()); + for (EventCompletionDetails e : events) + { + assertTrue(e.isEventComplete()); + assertEquals("roy", e.getEventCompletedBy()); + assertNotNull(e.getEventCompletedAt()); + } + + checkSearchAspect(recordFolder); + + // Test undo + + params = new HashMap(1); + params.put(CompleteEventAction.PARAM_EVENT_NAME, events.get(2).getEventName()); + this.rmActionService.executeRecordsManagementAction(recordFolder, "undoEvent", params); + + txn.commit(); + txn = transactionService.getUserTransaction(false); + txn.begin(); + + assertFalse((Boolean)this.nodeService.getProperty(da.getNodeRef(), PROP_DISPOSITION_EVENTS_ELIGIBLE)); + + params = new HashMap(3); + params.put(CompleteEventAction.PARAM_EVENT_NAME, events.get(2).getEventName()); + params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_AT, new Date()); + params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_BY, "roy"); + + this.rmActionService.executeRecordsManagementAction(recordFolder, "completeEvent", params); + + txn.commit(); + txn = transactionService.getUserTransaction(false); + txn.begin(); + + assertTrue((Boolean)this.nodeService.getProperty(da.getNodeRef(), PROP_DISPOSITION_EVENTS_ELIGIBLE)); + + // Do the commit action + this.rmActionService.executeRecordsManagementAction(recordFolder, "cutoff", null); + + txn.commit(); + txn = transactionService.getUserTransaction(false); + txn.begin(); + + // Check events are gone + da = dispositionService.getNextDispositionAction(recordFolder); + + assertNotNull(da); + assertEquals("destroy", da.getDispositionActionDefinition().getName()); + assertNotNull(da.getAsOfDate()); + assertFalse((Boolean)this.nodeService.getProperty(da.getNodeRef(), PROP_DISPOSITION_EVENTS_ELIGIBLE)); + events = da.getEventCompletionDetails(); + assertNotNull(events); + assertEquals(0, events.size()); + + checkSearchAspect(recordFolder); + + txn.commit(); + } + + private NodeRef createRecord(NodeRef recordFolder) + { + return createRecord(recordFolder, "MyRecord.txt"); + } + + private NodeRef createRecord(NodeRef recordFolder, String name) + { + return createRecord(recordFolder, name, "There is some content in this record"); + } + + private NodeRef createRecord(NodeRef recordFolder, String name, String someTextContent) + { + // Create the document + Map props = new HashMap(1); + props.put(ContentModel.PROP_NAME, name); + NodeRef recordOne = this.nodeService.createNode(recordFolder, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name), + ContentModel.TYPE_CONTENT, + props).getChildRef(); + + // Set the content + ContentWriter writer = this.contentService.getWriter(recordOne, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent(someTextContent); + + return recordOne; + } + + /** + * This method tests the filing of a custom type, as defined in DOD 5015. + */ + public void testFileDOD5015CustomTypes() throws Exception + { + NodeRef recordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Reports", "AIS Audit Records"); + + NodeRef recordFolder = createRecordFolder(recordCategory, "March AIS Audit Records"); + setComplete(); + endTransaction(); + + UserTransaction txn = transactionService.getUserTransaction(false); + txn.begin(); + + NodeRef testDocument = this.nodeService.createNode(recordFolder, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "CustomType"), + ContentModel.TYPE_CONTENT).getChildRef(); + + // It's not necessary to set content for this test. + + // File the record. + rmActionService.executeRecordsManagementAction(testDocument, "file"); + + assertTrue("testDocument should be a record.", rmService.isRecord(testDocument)); + + // Have the customType aspect applied.. + Map props = new HashMap(); + props.put(PROP_SCANNED_FORMAT.toPrefixString(serviceRegistry.getNamespaceService()), "f"); + props.put(PROP_SCANNED_FORMAT_VERSION.toPrefixString(serviceRegistry.getNamespaceService()), "1.0"); + props.put(PROP_RESOLUTION_X.toPrefixString(serviceRegistry.getNamespaceService()), "100"); + props.put(PROP_RESOLUTION_Y.toPrefixString(serviceRegistry.getNamespaceService()), "100"); + props.put(PROP_SCANNED_BIT_DEPTH.toPrefixString(serviceRegistry.getNamespaceService()), "10"); + rmActionService.executeRecordsManagementAction(testDocument, "applyScannedRecord", props); + + assertTrue("Custom type should have ScannedRecord aspect.", nodeService.hasAspect(testDocument, DOD5015Model.ASPECT_SCANNED_RECORD)); + + txn.rollback(); + } + + public void testFileDOD5015CustomTypes2() throws Exception + { + NodeRef recordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Reports", "AIS Audit Records"); + + NodeRef recordFolder = createRecordFolder(recordCategory, "March AIS Audit Records"); + setComplete(); + endTransaction(); + + UserTransaction txn = transactionService.getUserTransaction(false); + txn.begin(); + + NodeRef testDocument = this.nodeService.createNode(recordFolder, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "CustomType"), + ContentModel.TYPE_CONTENT).getChildRef(); + + // It's not necessary to set content for this test. + + // File the record + List aspects = new ArrayList(1); + aspects.add(DOD5015Model.ASPECT_SCANNED_RECORD); + Map props = new HashMap(1); + props.put(FileAction.PARAM_RECORD_METADATA_ASPECTS, (Serializable)aspects); + rmActionService.executeRecordsManagementAction(testDocument, "file", props); + + assertTrue("testDocument should be a record.", rmService.isRecord(testDocument)); + assertTrue("Custom type should have ScannedRecord aspect.", nodeService.hasAspect(testDocument, DOD5015Model.ASPECT_SCANNED_RECORD)); + + txn.rollback(); + } + + /** + * This method tests the filing of an already existing document i.e. one that is + * already contained within the document library. + */ + public void testFileFromDoclib() throws Exception + { + // Get the relevant RecordCategory and create a RecordFolder underneath it. + NodeRef recordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Reports", "AIS Audit Records"); + + NodeRef recordFolder = createRecordFolder(recordCategory, "March AIS Audit Records"); + setComplete(); + endTransaction(); + + UserTransaction txn = transactionService.getUserTransaction(false); + txn.begin(); + + // Unlike testBasicFilingTest, we now create a normal Alfresco content node + // rather than a fully-fledged record. The content must also be outside the + // fileplan. + + // Create a site - to put the content in. + final String rmTestSiteShortName = "rmTest" + System.currentTimeMillis(); + this.serviceRegistry.getSiteService().createSite("RMTestSite", rmTestSiteShortName, + "Test site for Records Management", "", SiteVisibility.PUBLIC); + + NodeRef siteRoot = this.serviceRegistry.getSiteService().getSite(rmTestSiteShortName).getNodeRef(); + NodeRef siteDocLib = this.nodeService.createNode(siteRoot, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "documentLibrary"), + ContentModel.TYPE_FOLDER).getChildRef(); + // Create the test document + NodeRef testDocument = this.nodeService.createNode(siteDocLib, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "PreexistingDocument.txt"), + ContentModel.TYPE_CONTENT).getChildRef(); + // Set some content + ContentWriter writer = this.contentService.getWriter(testDocument, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent("Some dummy content."); + + txn.commit(); + txn = transactionService.getUserTransaction(false); + txn.begin(); + + // Clearly, this should not be a record at this point. + assertFalse(this.nodeService.hasAspect(testDocument, ASPECT_RECORD)); + + // Now we want to file this document as a record within the RMA. + // To do this we simply move a document into the fileplan and file + this.serviceRegistry.getFileFolderService().move(testDocument, recordFolder, null); + rmActionService.executeRecordsManagementAction(testDocument, "file"); + + assertTrue("testDocument should be a record.", rmService.isRecord(testDocument)); + assertNotNull(this.nodeService.getProperty(testDocument, PROP_IDENTIFIER)); + assertNotNull(this.nodeService.getProperty(testDocument, PROP_DATE_FILED)); + + // Check the review schedule + assertTrue(this.nodeService.hasAspect(testDocument, ASPECT_VITAL_RECORD)); + assertNotNull(this.nodeService.getProperty(testDocument, PROP_REVIEW_AS_OF)); + + txn.commit(); + } + + /** + * This method tests the filing of non-electronic record. + */ + public void testFileNonElectronicRecord() throws Exception + { + setComplete(); + endTransaction(); + + // Create a record folder + final NodeRef recordFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + // Get the relevant RecordCategory and create a RecordFolder underneath it. + NodeRef recordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Reports", "AIS Audit Records"); + NodeRef result = createRecordFolder(recordCategory, "March AIS Audit Records" + System.currentTimeMillis()); + + return result; + } + }); + + // Create a non-electronic record + final NodeRef nonElectronicTestRecord = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + // Create the document + NodeRef result = nodeService.createNode(recordFolder, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "Non-electronic Record" + System.currentTimeMillis()), + RecordsManagementModel.TYPE_NON_ELECTRONIC_DOCUMENT).getChildRef(); + + // There is no content on a non-electronic record. + + // These properties are required in order to declare the record. + Map props = nodeService.getProperties(result); + props.put(RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "alfresco"); + props.put(RecordsManagementModel.PROP_ORIGINATOR, "admin"); + props.put(RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + + Calendar fileCalendar = Calendar.getInstance(); + String year = Integer.toString(fileCalendar.get(Calendar.YEAR)); + props.put(RecordsManagementModel.PROP_DATE_FILED, fileCalendar.getTime()); + + String recordId = year + "-" + nodeService.getProperty(result, ContentModel.PROP_NODE_DBID).toString(); + props.put(RecordsManagementModel.PROP_IDENTIFIER, recordId); + + + nodeService.setProperties(result, props); + + return result; + } + }); + + // File and declare the record + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + assertTrue("Expected non-electronic record to be a record.", rmService.isRecord(nonElectronicTestRecord)); + assertFalse("Expected non-electronic record not to be declared yet.", rmService.isRecordDeclared(nonElectronicTestRecord)); + + rmActionService.executeRecordsManagementAction(nonElectronicTestRecord, "declareRecord"); + + assertTrue("Non-electronic record should now be declared.", rmService.isRecordDeclared(nonElectronicTestRecord)); + + // These properties are added automatically when the record is filed + assertNotNull(nodeService.getProperty(nonElectronicTestRecord, RecordsManagementModel.PROP_IDENTIFIER)); + assertNotNull(nodeService.getProperty(nonElectronicTestRecord, RecordsManagementModel.PROP_DATE_FILED)); + +// assertNotNull(nodeService.getProperty(testRecord, ContentModel.PROP_TITLE)); +// assertNotNull(nodeService.getProperty(testRecord, RecordsManagementModel.PROP_SUPPLEMENTAL_MARKING_LIST)); +// assertNotNull(nodeService.getProperty(testRecord, RecordsManagementModel.PROP_MEDIA_TYPE)); +// assertNotNull(nodeService.getProperty(testRecord, RecordsManagementModel.PROP_FORMAT)); +// assertNotNull(nodeService.getProperty(testRecord, RecordsManagementModel.PROP_DATE_RECEIVED)); +// assertEquals("foo", nodeService.getProperty(testRecord, RecordsManagementModel.PROP_ADDRESS)); +// assertEquals("foo", nodeService.getProperty(testRecord, RecordsManagementModel.PROP_OTHER_ADDRESS)); +// assertNotNull(nodeService.getProperty(testRecord, RecordsManagementModel.PROP_LOCATION)); +// assertEquals("foo", nodeService.getProperty(testRecord, RecordsManagementModel.PROP_PROJECT_NAME)); + + //TODO Add links to other records as per test doc. + return null; + } + }); + } + + private NodeRef createRecordFolder(NodeRef recordCategory, String folderName) + { + Map folderProps = new HashMap(1); + folderProps.put(ContentModel.PROP_NAME, folderName); + NodeRef recordFolder = this.nodeService.createNode(recordCategory, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, folderName), + TYPE_RECORD_FOLDER).getChildRef(); + return recordFolder; + } + + /** + * Caveat Config + * + * @throws Exception + */ + public void testCaveatConfig() throws Exception + { + setComplete(); + endTransaction(); + + cleanCaveatConfigData(); + setupCaveatConfigData(); + + // set/reset allowed values (empty list by default) + + final List newValues = new ArrayList(4); + newValues.add(NOFORN); + newValues.add(NOCONTRACT); + newValues.add(FOUO); + newValues.add(FGI); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + rmAdminService.changeCustomConstraintValues(RecordsManagementCustomModel.CONSTRAINT_CUSTOM_SMLIST, newValues); + + return null; + } + }); + + final NodeRef recordFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + // Test list of allowed values for caveats + + List allowedValues = AuthenticationUtil.runAs(new RunAsWork>() + { + public List doWork() + { + // get allowed values for given caveat (for current user) + return caveatConfigService.getRMAllowedValues("rmc:smList"); + } + }, "dfranco"); + + assertEquals(2, allowedValues.size()); + assertTrue(allowedValues.contains(NOFORN)); + assertTrue(allowedValues.contains(FOUO)); + + + allowedValues = AuthenticationUtil.runAs(new RunAsWork>() + { + public List doWork() + { + // get allowed values for given caveat (for current user) + return caveatConfigService.getRMAllowedValues("rmc:smList"); + } + }, "dmartinz"); + + assertEquals(4, allowedValues.size()); + assertTrue(allowedValues.contains(NOFORN)); + assertTrue(allowedValues.contains(NOCONTRACT)); + assertTrue(allowedValues.contains(FOUO)); + assertTrue(allowedValues.contains(FGI)); + + + // Create record category / record folder + + final NodeRef recordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Reports", "AIS Audit Records"); + assertNotNull(recordCategory); + assertEquals("AIS Audit Records", nodeService.getProperty(recordCategory, ContentModel.PROP_NAME)); + + NodeRef recordFolder = createRecordFolder(recordCategory, "March AIS Audit Records"); + assertNotNull(recordFolder); + assertEquals(TYPE_RECORD_FOLDER, nodeService.getType(recordFolder)); + + // set RM capabilities on the file plan - to view & read records + setPermission(filePlan, PermissionService.ALL_AUTHORITIES, RMPermissionModel.VIEW_RECORDS, true); + setPermission(filePlan, PermissionService.ALL_AUTHORITIES, RMPermissionModel.READ_RECORDS, true); + + // set RM capabilities on the record folder - to read records + setPermission(recordFolder, PermissionService.ALL_AUTHORITIES, RMPermissionModel.READ_RECORDS, true); + + + return recordFolder; + } + }); + + final String RECORD_NAME = "MyRecord"+System.currentTimeMillis()+".txt"; + final String SOME_CONTENT = "There is some content in this record"; + + final NodeRef recordOne = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + int expectedChildCount = nodeService.getChildAssocs(recordFolder).size(); + + NodeRef recordOne = createRecord(recordFolder, RECORD_NAME, SOME_CONTENT); + + assertEquals(expectedChildCount+1, nodeService.getChildAssocs(recordFolder).size()); + + return recordOne; + } + }); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + assertTrue(nodeService.hasAspect(recordOne, ASPECT_RECORD)); + + int expectedChildCount = nodeService.getChildAssocs(recordFolder).size()-1; + + // + // Test caveats (security interceptors) BEFORE setting properties + // + + sanityCheckAccess("dmartinz", recordFolder, recordOne, RECORD_NAME, SOME_CONTENT, true, expectedChildCount); + sanityCheckAccess("gsmith", recordFolder, recordOne, RECORD_NAME, SOME_CONTENT, true, expectedChildCount); + sanityCheckAccess("dsandy", recordFolder, recordOne, RECORD_NAME, SOME_CONTENT, true, expectedChildCount); + + // Test setting properties (with restricted set of allowed values) + + // Set supplemental markings list (on record) + // TODO - set supplemental markings list (on record folder) + + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() + { + // set RM capabilities on the file plan - to file records and add/edit properties (ie. edit record) + setPermission(filePlan, "dfranco", RMPermissionModel.FILING, true); + setPermission(filePlan, "dfranco", RMPermissionModel.EDIT_RECORD_METADATA, true); + return null; + } + }, "admin"); + + + AuthenticationUtil.setFullyAuthenticatedUser("dfranco"); + assertEquals(AccessStatus.ALLOWED, publicServiceAccessService.hasAccess("NodeService", "exists", recordFolder)); + + return null; + } + }); + + try + { + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + + // Set smList + + Map propValues = new HashMap(1); + List smList = new ArrayList(3); + smList.add(FOUO); + smList.add(NOFORN); + smList.add(NOCONTRACT); + propValues.put(RecordsManagementModel.PROP_SUPPLEMENTAL_MARKING_LIST, (Serializable)smList); + nodeService.addProperties(recordOne, propValues); + + return null; + } + }); + + fail("Should fail with integrity exception"); // user 'dfranco' not allowed 'NOCONTRACT' + } + catch (IntegrityException ie) + { + // expected + } + + try + { + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // Set smList + + Map propValues = new HashMap(1); + List smList = new ArrayList(2); + smList.add(FOUO); + smList.add(NOFORN); + propValues.put(RecordsManagementModel.PROP_SUPPLEMENTAL_MARKING_LIST, (Serializable)smList); + nodeService.addProperties(recordOne, propValues); + + return null; + } + }); + } + catch (IntegrityException ie) + { + fail(""+ie); + } + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + @SuppressWarnings("unchecked") + List smList = (List)nodeService.getProperty(recordOne, RecordsManagementModel.PROP_SUPPLEMENTAL_MARKING_LIST); + assertEquals(2, smList.size()); + assertTrue(smList.contains(NOFORN)); + assertTrue(smList.contains(FOUO)); + + return null; + } + }); + + // User-defined field (in this case, "rmc:prjList" on record) + + // Create custom constraint (or reset values if it already exists) + + // create new custom constraint + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + try + { + List emptyList = new ArrayList(0); + rmAdminService.addCustomConstraintDefinition(CONSTRAINT_CUSTOM_PRJLIST, "Some Projects", true, emptyList, MatchLogic.AND); + } + catch (AlfrescoRuntimeException e) + { + // ignore - ie. assume exception is due to the fact that it already exists + } + + return null; + } + }); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + List newerValues = new ArrayList(3); + newerValues.add(PRJ_A); + newerValues.add(PRJ_B); + newerValues.add(PRJ_C); + + rmAdminService.changeCustomConstraintValues(CONSTRAINT_CUSTOM_PRJLIST, newerValues); + + return null; + } + }); + + // define custom property and reference custom constraint + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + try + { + // Define a custom "project list" property (for records) - note: multi-valued + rmAdminService.addCustomPropertyDefinition( + PROP_CUSTOM_PRJLIST, + ASPECT_RECORD, + PROP_CUSTOM_PRJLIST.getLocalName(), + DataTypeDefinition.TEXT, "Projects", + null, + null, + true, + false, + false, + CONSTRAINT_CUSTOM_PRJLIST); + } + catch (AlfrescoRuntimeException e) + { + // ignore - ie. assume exception is due to the fact that it already exists + } + + return null; + } + }); + + try + { + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + + // Set prjList + + Map propValues = new HashMap(1); + List prjList = new ArrayList(3); + prjList.add(PRJ_A); + prjList.add(PRJ_B); + propValues.put(PROP_CUSTOM_PRJLIST, (Serializable)prjList); + nodeService.addProperties(recordOne, propValues); + + return null; + } + }); + + fail("Should fail with integrity exception"); // user 'dfranco' not allowed 'Project B' + } + catch (IntegrityException ie) + { + // expected + } + + try + { + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // Set prjList + Map propValues = new HashMap(1); + List prjList = new ArrayList(3); + prjList.add(PRJ_A); + propValues.put(PROP_CUSTOM_PRJLIST, (Serializable)prjList); + nodeService.addProperties(recordOne, propValues); + + return null; + } + }); + } + catch (IntegrityException ie) + { + fail(""+ie); + } + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + @SuppressWarnings("unchecked") + List prjList = (List)nodeService.getProperty(recordOne, PROP_CUSTOM_PRJLIST); + assertEquals(1, prjList.size()); + assertTrue(prjList.contains(PRJ_A)); + + return null; + } + }); + + // + // Test caveats (security interceptors) AFTER setting properties + // + + int expectedChildCount = nodeService.getChildAssocs(recordFolder).size()-1; + sanityCheckAccess("dmartinz", recordFolder, recordOne, RECORD_NAME, SOME_CONTENT, true, expectedChildCount); + sanityCheckAccess("gsmith", recordFolder, recordOne, RECORD_NAME, SOME_CONTENT, false, expectedChildCount); // denied by rma:prjList ("Project A") + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + addToGroup("gsmith", "Engineering"); + + return null; + } + }); + + sanityCheckAccess("gsmith", recordFolder, recordOne, RECORD_NAME, SOME_CONTENT, true, expectedChildCount); + sanityCheckAccess("dsandy", recordFolder, recordOne, RECORD_NAME, SOME_CONTENT, false, expectedChildCount); // denied by rma:smList ("NOFORN", "FOUO") + + cleanCaveatConfigData(); + } + + private void setPermission(NodeRef nodeRef, String authority, String permission, boolean allow) + { + permissionService.setPermission(nodeRef, authority, permission, allow); + if (permission.equals(RMPermissionModel.FILING)) + { + if (rmService.isRecordCategory(nodeRef) == true) + { + List assocs = nodeService.getChildAssocs(nodeRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef assoc : assocs) + { + NodeRef child = assoc.getChildRef(); + if (rmService.isRecordFolder(child) == true || rmService.isRecordCategory(child) == true) + { + setPermission(child, authority, permission, allow); + } + } + } + } + } + + private void cleanCaveatConfigData() + { + startNewTransaction(); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + + deleteUser("jrangel"); + deleteUser("dmartinz"); + deleteUser("jrogers"); + deleteUser("hmcneil"); + deleteUser("dfranco"); + deleteUser("gsmith"); + deleteUser("eharris"); + deleteUser("bbayless"); + deleteUser("mhouse"); + deleteUser("aly"); + deleteUser("dsandy"); + deleteUser("driggs"); + deleteUser("test1"); + + deleteGroup("Engineering"); + deleteGroup("Finance"); + deleteGroup("test1"); + + caveatConfigService.updateOrCreateCaveatConfig("{}"); // empty config ! + + setComplete(); + endTransaction(); + } + + private void setupCaveatConfigData() + { + startNewTransaction(); + + // Switch to admin + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + + // Create test users/groups (if they do not already exist) + + createUser("jrangel"); + createUser("dmartinz"); + createUser("jrogers"); + createUser("hmcneil"); + createUser("dfranco"); + createUser("gsmith"); + createUser("eharris"); + createUser("bbayless"); + createUser("mhouse"); + createUser("aly"); + createUser("dsandy"); + createUser("driggs"); + createUser("test1"); + + createGroup("Engineering"); + createGroup("Finance"); + createGroup("test1"); + + addToGroup("jrogers", "Engineering"); + addToGroup("dfranco", "Finance"); + + // not in grouo to start with - added later + //addToGroup("gsmith", "Engineering"); + + File file = new File(System.getProperty("user.dir")+"/test-resources/testCaveatConfig2.json"); // from test-resources + assertTrue(file.exists()); + + caveatConfigService.updateOrCreateCaveatConfig(file); + + setComplete(); + endTransaction(); + } + + protected void createUser(String userName) + { + if (! authenticationService.authenticationExists(userName)) + { + authenticationService.createAuthentication(userName, "PWD".toCharArray()); + } + + if (! personService.personExists(userName)) + { + PropertyMap ppOne = new PropertyMap(4); + ppOne.put(ContentModel.PROP_USERNAME, userName); + ppOne.put(ContentModel.PROP_FIRSTNAME, "firstName"); + ppOne.put(ContentModel.PROP_LASTNAME, "lastName"); + ppOne.put(ContentModel.PROP_EMAIL, "email@email.com"); + ppOne.put(ContentModel.PROP_JOBTITLE, "jobTitle"); + + personService.createPerson(ppOne); + } + } + + protected void deleteUser(String userName) + { + if (personService.personExists(userName)) + { + personService.deletePerson(userName); + } + } + + protected void createGroup(String groupShortName) + { + createGroup(null, groupShortName); + } + + protected void createGroup(String parentGroupShortName, String groupShortName) + { + if (parentGroupShortName != null) + { + String parentGroupFullName = authorityService.getName(AuthorityType.GROUP, parentGroupShortName); + if (authorityService.authorityExists(parentGroupFullName) == false) + { + authorityService.createAuthority(AuthorityType.GROUP, groupShortName, groupShortName, null); + authorityService.addAuthority(parentGroupFullName, groupShortName); + } + } + else + { + authorityService.createAuthority(AuthorityType.GROUP, groupShortName, groupShortName, null); + } + } + + protected void deleteGroup(String groupShortName) + { + String groupFullName = authorityService.getName(AuthorityType.GROUP, groupShortName); + if (authorityService.authorityExists(groupFullName) == true) + { + authorityService.deleteAuthority(groupFullName); + } + } + + protected void addToGroup(String authorityName, String groupShortName) + { + authorityService.addAuthority(authorityService.getName(AuthorityType.GROUP, groupShortName), authorityName); + } + + protected void removeFromGroup(String authorityName, String groupShortName) + { + authorityService.removeAuthority(authorityService.getName(AuthorityType.GROUP, groupShortName), authorityName); + } + + private void sanityCheckAccess(String user, NodeRef recordFolder, NodeRef record, String expectedName, String expectedContent, boolean expectedAllowed, int baseCount) + { + //startNewTransaction(); + + AuthenticationUtil.setFullyAuthenticatedUser(user); + + // Sanity check search service - eg. query + + String query = "ID:"+AbstractLuceneQueryParser.escape(record.toString()); + ResultSet rs = this.searchService.query(SPACES_STORE, SearchService.LANGUAGE_LUCENE, query); + + if (expectedAllowed) + { + assertEquals(1, rs.length()); + assertEquals(record.toString(), rs.getNodeRef(0).toString()); + } + else + { + assertEquals(0, rs.length()); + } + rs.close(); + + // Sanity check node service - eg. getProperty, getChildAssocs + + try + { + Serializable value = this.nodeService.getProperty(record, ContentModel.PROP_NAME); + + if (expectedAllowed) + { + assertNotNull(value); + assertEquals(expectedName, (String)value); + } + else + { + fail("Unexpected - access should be denied by caveats"); + } + } + catch (AccessDeniedException ade) + { + if (expectedAllowed) + { + fail("Unexpected - access should be allowed by caveats"); + } + + // expected + } + + List childAssocs = nodeService.getChildAssocs(recordFolder); + + if (expectedAllowed) + { + assertEquals(baseCount+1, childAssocs.size()); + assertEquals(record.toString(), childAssocs.get(baseCount).getChildRef().toString()); + } + else + { + assertEquals(baseCount, childAssocs.size()); + } + + // Sanity check content service - eg. getReader + + try + { + ContentReader reader = this.contentService.getReader(record, ContentModel.PROP_CONTENT); + + if (expectedAllowed) + { + assertNotNull(reader); + assertEquals(expectedContent, reader.getContentString()); + } + else + { + fail("Unexpected - access should be denied by caveats"); + } + } + catch (AccessDeniedException ade) + { + if (expectedAllowed) + { + fail("Unexpected - access should be allowed by caveats"); + } + + // expected + } + + //setComplete(); + //endTransaction(); + } + + /** + * https://issues.alfresco.com/jira/browse/ETHREEOH-3587 + */ + public void testETHREEOH3587() + { + NodeRef recordFolder = TestUtilities.getRecordFolder(rmService, nodeService, "Reports", "AIS Audit Records", "January AIS Audit Records"); + assertNotNull(recordFolder); + + // Create a record + final NodeRef record = createRecord(recordFolder, GUID.generate()); + + // Commit in order to trigger the setUpRecordFolder behaviour + setComplete(); + endTransaction(); + + // Now try and update the id, this should fail + try + { + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // Lets just check the record identifier has been set + String id = (String)nodeService.getProperty(record, RecordsManagementModel.PROP_IDENTIFIER); + assertNotNull(id); + + nodeService.setProperty(record, RecordsManagementModel.PROP_IDENTIFIER, "randomValue"); + + return null; + } + }); + + fail("You should not be allowed to update the identifier of a record once it has been created."); + } + catch(AlfrescoRuntimeException e) + { + // Expected + } + + // TODO set the identifier of the second record to be the same as the first .... + } + + /** + * Vital Record Test + * + * @throws Exception + */ + public void testVitalRecords() throws Exception + { + // + // Create a record folder under a "vital" category + // + + // TODO Don't think I need to do this. Can I reuse the existing January one? + + NodeRef vitalRecCategory = + TestUtilities.getRecordCategory(rmService, nodeService, "Reports", "AIS Audit Records"); + + assertNotNull(vitalRecCategory); + assertEquals("AIS Audit Records", + this.nodeService.getProperty(vitalRecCategory, ContentModel.PROP_NAME)); + + NodeRef vitalRecFolder = this.nodeService.createNode(vitalRecCategory, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, + "March AIS Audit Records"), + TYPE_RECORD_FOLDER).getChildRef(); + setComplete(); + endTransaction(); + UserTransaction txn1 = transactionService.getUserTransaction(false); + txn1.begin(); + + // Check the Vital Record data + VitalRecordDefinition vitalRecCatDefinition = vitalRecordService.getVitalRecordDefinition(vitalRecCategory); + assertNotNull("This record category should have a VitalRecordDefinition", vitalRecCatDefinition); + assertTrue(vitalRecCatDefinition.isEnabled()); + + VitalRecordDefinition vitalRecFolderDefinition = vitalRecordService.getVitalRecordDefinition(vitalRecFolder); + assertNotNull("This record folder should have a VitalRecordDefinition", vitalRecFolderDefinition); + assertTrue(vitalRecFolderDefinition.isEnabled()); + + assertEquals("The Vital Record reviewPeriod in the folder did not match its parent category", + vitalRecFolderDefinition.getReviewPeriod(), + vitalRecCatDefinition.getReviewPeriod()); + + // check the search aspect for both the category and folder + checkSearchAspect(vitalRecFolder); + + // Create a vital record + NodeRef vitalRecord = this.nodeService.createNode(vitalRecFolder, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, + "MyVitalRecord" + System.currentTimeMillis() +".txt"), + ContentModel.TYPE_CONTENT).getChildRef(); + + // Set the content + ContentWriter writer = this.contentService.getWriter(vitalRecord, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent("There is some content in this record"); + + rmActionService.executeRecordsManagementAction(vitalRecord, "file"); + + txn1.commit(); + + UserTransaction txn2 = transactionService.getUserTransaction(false); + txn2.begin(); + + // Check the review schedule + + assertTrue(this.nodeService.hasAspect(vitalRecord, ASPECT_VITAL_RECORD)); + VitalRecordDefinition vitalRecDefinition = vitalRecordService.getVitalRecordDefinition(vitalRecord); + assertTrue(vitalRecDefinition.isEnabled()); + Date vitalRecordAsOfDate = (Date)this.nodeService.getProperty(vitalRecord, PROP_REVIEW_AS_OF); + assertNotNull("vitalRecord should have a reviewAsOf date.", vitalRecordAsOfDate); + + // check the search aspect for the vital record + checkSearchAspect(vitalRecord); + + // + // Create a record folder under a "non-vital" category + // + NodeRef nonVitalRecordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Reports", "Unit Manning Documents"); + assertNotNull(nonVitalRecordCategory); + assertEquals("Unit Manning Documents", this.nodeService.getProperty(nonVitalRecordCategory, ContentModel.PROP_NAME)); + + NodeRef nonVitalFolder = this.nodeService.createNode(nonVitalRecordCategory, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "4th Quarter Unit Manning Documents"), + TYPE_RECORD_FOLDER).getChildRef(); + txn2.commit(); + + UserTransaction txn3 = transactionService.getUserTransaction(false); + txn3.begin(); + + // Check the Vital Record data + assertFalse(vitalRecordService.getVitalRecordDefinition(nonVitalRecordCategory).isEnabled()); + assertFalse(vitalRecordService.getVitalRecordDefinition(nonVitalFolder).isEnabled()); + assertEquals("The Vital Record reviewPeriod in the folder did not match its parent category", + vitalRecordService.getVitalRecordDefinition(nonVitalFolder).getReviewPeriod(), + vitalRecordService.getVitalRecordDefinition(nonVitalRecordCategory).getReviewPeriod()); + + // Create a record + NodeRef nonVitalRecord = this.nodeService.createNode(nonVitalFolder, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "MyNonVitalRecord.txt"), + ContentModel.TYPE_CONTENT).getChildRef(); + + // Set content + writer = this.contentService.getWriter(nonVitalRecord, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent("There is some content in this record"); + + this.rmActionService.executeRecordsManagementAction(nonVitalRecord, "file"); + + txn3.commit(); + + UserTransaction txn4 = transactionService.getUserTransaction(false); + txn4.begin(); + + // Check the review schedule + assertFalse(this.nodeService.hasAspect(nonVitalRecord, ASPECT_VITAL_RECORD)); + assertFalse(vitalRecordService.getVitalRecordDefinition(nonVitalRecord).isEnabled()); + assertEquals("The Vital Record reviewPeriod did not match its parent category", + vitalRecordService.getVitalRecordDefinition(nonVitalRecord).getReviewPeriod(), + vitalRecordService.getVitalRecordDefinition(nonVitalFolder).getReviewPeriod()); + + // Declare as a record + assertTrue(this.nodeService.hasAspect(nonVitalRecord, ASPECT_RECORD)); + + assertTrue("Declared record already on prior to test", + this.nodeService.hasAspect(nonVitalRecord, ASPECT_DECLARED_RECORD) == false); + + + this.nodeService.setProperty(nonVitalRecord, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + List smList = new ArrayList(2); + smList.add(FOUO); + smList.add(NOFORN); + this.nodeService.setProperty(nonVitalRecord, RecordsManagementModel.PROP_SUPPLEMENTAL_MARKING_LIST, (Serializable)smList); + this.nodeService.setProperty(nonVitalRecord, RecordsManagementModel.PROP_MEDIA_TYPE, "mediaTypeValue"); + this.nodeService.setProperty(nonVitalRecord, RecordsManagementModel.PROP_FORMAT, "formatValue"); + this.nodeService.setProperty(nonVitalRecord, RecordsManagementModel.PROP_DATE_RECEIVED, new Date()); + this.nodeService.setProperty(nonVitalRecord, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + this.nodeService.setProperty(nonVitalRecord, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + this.nodeService.setProperty(nonVitalRecord, ContentModel.PROP_TITLE, "titleValue"); + + this.rmActionService.executeRecordsManagementAction(nonVitalRecord, "declareRecord"); + assertTrue(this.nodeService.hasAspect(nonVitalRecord, ASPECT_RECORD)); + assertTrue("Declared aspect not set", this.nodeService.hasAspect(nonVitalRecord, ASPECT_DECLARED_RECORD)); + + // + // Now we will change the vital record indicator in the containers above these records + // and ensure that the change is reflected down to the record. + // + + // 1. Switch parent folder from non-vital to vital. + this.nodeService.setProperty(nonVitalFolder, PROP_VITAL_RECORD_INDICATOR, true); + this.nodeService.setProperty(nonVitalFolder, PROP_REVIEW_PERIOD, "week|1"); + + txn4.commit(); + + UserTransaction txn5 = transactionService.getUserTransaction(false); + txn5.begin(); + + // check the folder search aspect + checkSearchAspect(nonVitalFolder); + + NodeRef formerlyNonVitalRecord = nonVitalRecord; + + assertTrue("Expected VitalRecord aspect not present", nodeService.hasAspect(formerlyNonVitalRecord, ASPECT_VITAL_RECORD)); + VitalRecordDefinition formerlyNonVitalRecordDefinition = vitalRecordService.getVitalRecordDefinition(formerlyNonVitalRecord); + assertNotNull(formerlyNonVitalRecordDefinition); + + assertEquals("The Vital Record reviewPeriod is wrong.", new Period("week|1"), + vitalRecordService.getVitalRecordDefinition(formerlyNonVitalRecord).getReviewPeriod()); + assertNotNull("formerlyNonVitalRecord should now have a reviewAsOf date.", + nodeService.getProperty(formerlyNonVitalRecord, PROP_REVIEW_AS_OF)); + + // check search aspect for the new vital record + checkSearchAspect(formerlyNonVitalRecord); + + // 2. Switch parent folder from vital to non-vital. + this.nodeService.setProperty(vitalRecFolder, PROP_VITAL_RECORD_INDICATOR, false); + + txn5.commit(); + + UserTransaction txn6 = transactionService.getUserTransaction(false); + txn6.begin(); + + NodeRef formerlyVitalRecord = vitalRecord; + + assertTrue("Unexpected VitalRecord aspect present", + nodeService.hasAspect(formerlyVitalRecord, ASPECT_VITAL_RECORD) == false); + VitalRecordDefinition formerlyVitalRecordDefinition = vitalRecordService.getVitalRecordDefinition(formerlyVitalRecord); + assertNotNull(formerlyVitalRecordDefinition); + assertNull("formerlyVitalRecord should now not have a reviewAsOf date.", + nodeService.getProperty(formerlyVitalRecord, PROP_REVIEW_AS_OF)); + + // 3. override the VitalRecordDefinition between Category, Folder, Record and ensure + // the overrides work + + // First switch the non-vital record folder back to vital. + this.nodeService.setProperty(vitalRecFolder, PROP_VITAL_RECORD_INDICATOR, true); + + txn6.commit(); + UserTransaction txn7 = transactionService.getUserTransaction(false); + txn7.begin(); + + assertTrue("Unexpected VitalRecord aspect present", + nodeService.hasAspect(vitalRecord, ASPECT_VITAL_RECORD)); + + // The reviewAsOf date should be changing as the parent review periods are updated. + Date initialReviewAsOfDate = (Date)nodeService.getProperty(vitalRecord, PROP_REVIEW_AS_OF); + assertNotNull("record should have a reviewAsOf date.", + initialReviewAsOfDate); + + // Change some of the VitalRecordDefinition in Record Category + Map recCatProps = this.nodeService.getProperties(vitalRecCategory); + + // Run this test twice (after a clean db) and it fails at the below line. + assertEquals(new Period("week|1"), recCatProps.get(PROP_REVIEW_PERIOD)); + this.nodeService.setProperty(vitalRecCategory, PROP_REVIEW_PERIOD, new Period("day|1")); + + txn7.commit(); + UserTransaction txn8 = transactionService.getUserTransaction(false); + txn8.begin(); + + assertEquals(new Period("day|1"), vitalRecordService.getVitalRecordDefinition(vitalRecCategory).getReviewPeriod()); + assertEquals(new Period("day|1"), vitalRecordService.getVitalRecordDefinition(vitalRecFolder).getReviewPeriod()); + + // check the search aspect of the folder after period change + checkSearchAspect(vitalRecFolder); + + // Change some of the VitalRecordDefinition in Record Folder + Map folderProps = this.nodeService.getProperties(vitalRecFolder); + assertEquals(new Period("day|1"), folderProps.get(PROP_REVIEW_PERIOD)); + this.nodeService.setProperty(vitalRecFolder, PROP_REVIEW_PERIOD, new Period("month|1")); + + txn8.commit(); + UserTransaction txn9 = transactionService.getUserTransaction(false); + txn9.begin(); + + assertEquals(new Period("day|1"), vitalRecordService.getVitalRecordDefinition(vitalRecCategory).getReviewPeriod()); + assertEquals(new Period("month|1"), vitalRecordService.getVitalRecordDefinition(vitalRecFolder).getReviewPeriod()); + + // check the search aspect of the folder after period change + checkSearchAspect(vitalRecFolder); + + // Need to commit the transaction to trigger the behaviour that handles changes to VitalRecord Definition. + txn9.commit(); + UserTransaction txn10 = transactionService.getUserTransaction(false); + txn10.begin(); + + Date newReviewAsOfDate = (Date)nodeService.getProperty(vitalRecord, PROP_REVIEW_AS_OF); + assertNotNull("record should have a reviewAsOf date.", initialReviewAsOfDate); + assertTrue("reviewAsOfDate should have changed.", + initialReviewAsOfDate.toString().equals(newReviewAsOfDate.toString()) == false); + + // check the search aspect of the record after period change + checkSearchAspect(vitalRecord); + + // Now clean up after this test. + nodeService.deleteNode(vitalRecord); + nodeService.deleteNode(vitalRecFolder); + nodeService.deleteNode(nonVitalRecord); + nodeService.deleteNode(nonVitalFolder); + nodeService.setProperty(vitalRecCategory, PROP_REVIEW_PERIOD, new Period("week|1")); + + txn10.commit(); + } + +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/JScriptTestSuite.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/JScriptTestSuite.java new file mode 100644 index 0000000000..540127ad94 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/JScriptTestSuite.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.alfresco.module.org_alfresco_module_rm.test.jscript.RMJScriptTest; + + +/** + * RM JScript test suite + * + * @author Roy Wetherall + */ +public class JScriptTestSuite extends TestSuite +{ + /** + * Creates the test suite + * + * @return the test suite + */ + public static Test suite() + { + TestSuite suite = new TestSuite(); + suite.addTestSuite(RMJScriptTest.class); + return suite; + } +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/ServicesTestSuite.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/ServicesTestSuite.java new file mode 100644 index 0000000000..e76876eab6 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/ServicesTestSuite.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test; + +import org.alfresco.module.org_alfresco_module_rm.test.service.DispositionServiceImplTest; +import org.alfresco.module.org_alfresco_module_rm.test.service.RecordsManagementAdminServiceImplTest; +import org.alfresco.module.org_alfresco_module_rm.test.service.RecordsManagementSearchServiceImplTest; +import org.alfresco.module.org_alfresco_module_rm.test.service.RecordsManagementServiceImplTest; +import org.alfresco.module.org_alfresco_module_rm.test.service.VitalRecordServiceImplTest; + +import junit.framework.Test; +import junit.framework.TestSuite; + + +/** + * RM test suite + * + * @author Roy Wetherall + */ +public class ServicesTestSuite extends TestSuite +{ + /** + * Creates the test suite + * + * @return the test suite + */ + public static Test suite() + { + TestSuite suite = new TestSuite(); + suite.addTestSuite(RecordsManagementServiceImplTest.class); + suite.addTestSuite(DispositionServiceImplTest.class); + //suite.addTestSuite(RecordsManagementActionServiceImplTest.class); + suite.addTestSuite(RecordsManagementAdminServiceImplTest.class); + //suite.addTestSuite(RecordsManagementAuditServiceImplTest.class); + //suite.addTestSuite(RecordsManagementEventServiceImplTest.class); + //suite.addTestSuite(RecordsManagementSecurityServiceImplTest.class); + suite.addTestSuite(RecordsManagementSearchServiceImplTest.class); + suite.addTestSuite(VitalRecordServiceImplTest.class); + return suite; + } +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/WebScriptTestSuite.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/WebScriptTestSuite.java new file mode 100644 index 0000000000..a4532b09c6 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/WebScriptTestSuite.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.alfresco.module.org_alfresco_module_rm.test.webscript.BootstraptestDataRestApiTest; +import org.alfresco.module.org_alfresco_module_rm.test.webscript.DispositionRestApiTest; +import org.alfresco.module.org_alfresco_module_rm.test.webscript.EventRestApiTest; +import org.alfresco.module.org_alfresco_module_rm.test.webscript.RMCaveatConfigScriptTest; +import org.alfresco.module.org_alfresco_module_rm.test.webscript.RMConstraintScriptTest; +import org.alfresco.module.org_alfresco_module_rm.test.webscript.RmRestApiTest; +import org.alfresco.module.org_alfresco_module_rm.test.webscript.RoleRestApiTest; + + +/** + * RM WebScript test suite + * + * @author Roy Wetherall + */ +public class WebScriptTestSuite extends TestSuite +{ + /** + * Creates the test suite + * + * @return the test suite + */ + public static Test suite() + { + TestSuite suite = new TestSuite(); + suite.addTestSuite(BootstraptestDataRestApiTest.class); + suite.addTestSuite(DispositionRestApiTest.class); + suite.addTestSuite(EventRestApiTest.class); + suite.addTestSuite(RMCaveatConfigScriptTest.class); + suite.addTestSuite(RMConstraintScriptTest.class); + suite.addTestSuite(RmRestApiTest.class); + suite.addTestSuite(RoleRestApiTest.class); + return suite; + } +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/capabilities/CapabilitiesTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/capabilities/CapabilitiesTest.java new file mode 100644 index 0000000000..5ae06de2a9 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/capabilities/CapabilitiesTest.java @@ -0,0 +1,3842 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.capabilities; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.module.org_alfresco_module_rm.capability.Capability; +import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.permissions.PermissionReference; +import org.alfresco.repo.security.permissions.impl.model.PermissionModel; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.AccessPermission; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.security.PermissionService; + +/** + * Test the RM permissions model + * + * @author Andy Hind + * @author Roy Wetherall + */ +public class CapabilitiesTest extends BaseRMTestCase implements + RMPermissionModel, RecordsManagementModel +{ + private NodeRef record; + + private PermissionModel permissionModel; + private PermissionService permissionService; + + @Override + protected void initServices() + { + super.initServices(); + + permissionModel = (PermissionModel) applicationContext.getBean("permissionsModelDAO"); + permissionService = (PermissionService) applicationContext.getBean("PermissionService"); + } + + @Override + protected boolean isUserTest() + { + return true; + } + + @Override + protected void setupTestDataImpl() + { + super.setupTestDataImpl(); + + record = utils.createRecord(rmFolder, "CapabilitiesTest.txt"); + } + + @Override + protected void setupTestUsersImpl(NodeRef filePlan) + { + super.setupTestUsersImpl(filePlan); + + // Give all the users file permission objects + for (String user : testUsers) + { + securityService.setPermission(filePlan, user, FILING); + securityService.setPermission(rmContainer, user, FILING); + } + } + + protected void check(Map access, String name, AccessStatus accessStatus) + { + Capability capability = securityService.getCapability(name); + assertNotNull(capability); + assertEquals(accessStatus, access.get(capability)); + } + + /** + * Check the RM permission model + */ + public void testPermissionsModel() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + // As system user + AuthenticationUtil + .setFullyAuthenticatedUser(AuthenticationUtil + .getSystemUserName()); + + Set exposed = permissionModel + .getExposedPermissions(ASPECT_FILE_PLAN_COMPONENT); + assertEquals(6, exposed.size()); + assertTrue(exposed.contains(permissionModel + .getPermissionReference( + ASPECT_FILE_PLAN_COMPONENT, + ROLE_ADMINISTRATOR))); + + // Check all the permission are there + Set all = permissionModel + .getAllPermissions(ASPECT_FILE_PLAN_COMPONENT); + assertEquals(58 /* capbilities */* 2 + 5 /* roles */ + + (2 /* Read+File */* 2) + 1 /* Filing */, all + .size()); + + /* + * Check the granting for each permission. It is assumed + * that the ROLE_ADMINISTRATOR always has grant + * permission so is automatically checked. + */ + checkGranting(ACCESS_AUDIT, ROLE_RECORDS_MANAGER); + checkGranting(ADD_MODIFY_EVENT_DATES, + ROLE_RECORDS_MANAGER, ROLE_SECURITY_OFFICER, + ROLE_POWER_USER); + checkGranting(APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + ROLE_RECORDS_MANAGER); + checkGranting(ATTACH_RULES_TO_METADATA_PROPERTIES, + ROLE_RECORDS_MANAGER); + checkGranting(AUTHORIZE_ALL_TRANSFERS, + ROLE_RECORDS_MANAGER); + checkGranting(AUTHORIZE_NOMINATED_TRANSFERS, + ROLE_RECORDS_MANAGER); + checkGranting(CHANGE_OR_DELETE_REFERENCES, + ROLE_RECORDS_MANAGER); + checkGranting(CLOSE_FOLDERS, ROLE_RECORDS_MANAGER, + ROLE_SECURITY_OFFICER, ROLE_POWER_USER); + checkGranting(CREATE_AND_ASSOCIATE_SELECTION_LISTS, + ROLE_RECORDS_MANAGER); + checkGranting( + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + ROLE_RECORDS_MANAGER, ROLE_SECURITY_OFFICER); + checkGranting(CREATE_MODIFY_DESTROY_EVENTS, + ROLE_RECORDS_MANAGER); + checkGranting(CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + ROLE_RECORDS_MANAGER); + checkGranting(CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + ROLE_RECORDS_MANAGER); + checkGranting(CREATE_MODIFY_DESTROY_FOLDERS, + ROLE_RECORDS_MANAGER, ROLE_SECURITY_OFFICER, + ROLE_POWER_USER); + checkGranting(CREATE_MODIFY_DESTROY_RECORD_TYPES, + ROLE_RECORDS_MANAGER); + checkGranting(CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + ROLE_RECORDS_MANAGER); + checkGranting(CREATE_MODIFY_DESTROY_ROLES, + ROLE_RECORDS_MANAGER); + checkGranting(CREATE_MODIFY_DESTROY_TIMEFRAMES, + ROLE_RECORDS_MANAGER); + checkGranting(CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + ROLE_RECORDS_MANAGER); + checkGranting(CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + ROLE_RECORDS_MANAGER); + checkGranting(CYCLE_VITAL_RECORDS, + ROLE_RECORDS_MANAGER, ROLE_SECURITY_OFFICER, + ROLE_POWER_USER); + checkGranting(DECLARE_AUDIT_AS_RECORD, + ROLE_RECORDS_MANAGER); + checkGranting(DECLARE_RECORDS, ROLE_RECORDS_MANAGER, + ROLE_SECURITY_OFFICER, ROLE_POWER_USER, + ROLE_USER); + checkGranting(DECLARE_RECORDS_IN_CLOSED_FOLDERS, + ROLE_RECORDS_MANAGER, ROLE_SECURITY_OFFICER, + ROLE_POWER_USER); + checkGranting(DELETE_AUDIT, ROLE_RECORDS_MANAGER); + checkGranting(DELETE_LINKS, ROLE_RECORDS_MANAGER); + checkGranting(DELETE_RECORDS, ROLE_RECORDS_MANAGER); + checkGranting(DESTROY_RECORDS, ROLE_RECORDS_MANAGER); + checkGranting( + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + ROLE_RECORDS_MANAGER); + checkGranting(DISPLAY_RIGHTS_REPORT, + ROLE_RECORDS_MANAGER); + checkGranting(EDIT_DECLARED_RECORD_METADATA, + ROLE_RECORDS_MANAGER); + checkGranting(EDIT_NON_RECORD_METADATA, + ROLE_RECORDS_MANAGER, ROLE_SECURITY_OFFICER, + ROLE_POWER_USER); + checkGranting(EDIT_RECORD_METADATA, + ROLE_RECORDS_MANAGER, ROLE_SECURITY_OFFICER, + ROLE_POWER_USER); + checkGranting(EDIT_SELECTION_LISTS, + ROLE_RECORDS_MANAGER); + checkGranting(ENABLE_DISABLE_AUDIT_BY_TYPES, + ROLE_RECORDS_MANAGER); + checkGranting(EXPORT_AUDIT, ROLE_RECORDS_MANAGER); + checkGranting(EXTEND_RETENTION_PERIOD_OR_FREEZE, + ROLE_RECORDS_MANAGER); + checkGranting(MAKE_OPTIONAL_PARAMETERS_MANDATORY, + ROLE_RECORDS_MANAGER); + checkGranting(MANAGE_ACCESS_CONTROLS); + checkGranting(MANAGE_ACCESS_RIGHTS, + ROLE_RECORDS_MANAGER); + checkGranting(MANUALLY_CHANGE_DISPOSITION_DATES, + ROLE_RECORDS_MANAGER); + checkGranting(MAP_CLASSIFICATION_GUIDE_METADATA, + ROLE_RECORDS_MANAGER); + checkGranting(MAP_EMAIL_METADATA, ROLE_RECORDS_MANAGER); + checkGranting(MOVE_RECORDS, ROLE_RECORDS_MANAGER); + checkGranting(PASSWORD_CONTROL, ROLE_RECORDS_MANAGER); + checkGranting(PLANNING_REVIEW_CYCLES, + ROLE_RECORDS_MANAGER, ROLE_SECURITY_OFFICER, + ROLE_POWER_USER); + checkGranting(RE_OPEN_FOLDERS, ROLE_RECORDS_MANAGER, + ROLE_SECURITY_OFFICER, ROLE_POWER_USER); + checkGranting(SELECT_AUDIT_METADATA, + ROLE_RECORDS_MANAGER); + checkGranting(TRIGGER_AN_EVENT, ROLE_RECORDS_MANAGER); + checkGranting(UNDECLARE_RECORDS, ROLE_RECORDS_MANAGER); + checkGranting(UNFREEZE, ROLE_RECORDS_MANAGER); + checkGranting(UPDATE_CLASSIFICATION_DATES, + ROLE_RECORDS_MANAGER, ROLE_SECURITY_OFFICER); + checkGranting(UPDATE_EXEMPTION_CATEGORIES, + ROLE_RECORDS_MANAGER, ROLE_SECURITY_OFFICER); + checkGranting(UPDATE_TRIGGER_DATES, + ROLE_RECORDS_MANAGER); + checkGranting(UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + ROLE_RECORDS_MANAGER); + checkGranting(UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + ROLE_RECORDS_MANAGER, ROLE_SECURITY_OFFICER); + checkGranting(VIEW_RECORDS, ROLE_RECORDS_MANAGER, + ROLE_SECURITY_OFFICER, ROLE_POWER_USER, + ROLE_USER); + checkGranting(VIEW_UPDATE_REASONS_FOR_FREEZE, + ROLE_RECORDS_MANAGER); + + return null; + } + }, false, true); + } + + /** + * Check that the roles passed have grant on the permission passed. + * + * @param permission + * permission + * @param roles + * grant roles + */ + private void checkGranting(String permission, String... roles) + { + Set granting = permissionModel + .getGrantingPermissions(permissionModel.getPermissionReference( + RecordsManagementModel.ASPECT_FILE_PLAN_COMPONENT, + permission)); + Set test = new HashSet(); + test.addAll(granting); + Set nonRM = new HashSet(); + for (PermissionReference pr : granting) + { + if (!pr.getQName().equals( + RecordsManagementModel.ASPECT_FILE_PLAN_COMPONENT)) + { + nonRM.add(pr); + } + } + test.removeAll(nonRM); + assertEquals(roles.length + 2, test.size()); + + assertTrue(test.contains(permissionModel.getPermissionReference( + RecordsManagementModel.ASPECT_FILE_PLAN_COMPONENT, + ROLE_ADMINISTRATOR))); + for (String role : roles) + { + assertTrue(test.contains(permissionModel.getPermissionReference( + RecordsManagementModel.ASPECT_FILE_PLAN_COMPONENT, role))); + } + + } + + /** + * Test the capability configuration + */ + public void testConfig() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + // As system user + AuthenticationUtil + .setFullyAuthenticatedUser(AuthenticationUtil + .getSystemUserName()); + + assertEquals(6, securityService.getProtectedAspects() + .size()); + assertEquals(13, securityService + .getProtectedProperties().size()); + + // Test action wire up + testCapabilityActions(0, ACCESS_AUDIT); + testCapabilityActions(2, ADD_MODIFY_EVENT_DATES); + testCapabilityActions(2, + APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF); + testCapabilityActions(0, + ATTACH_RULES_TO_METADATA_PROPERTIES); + testCapabilityActions(2, AUTHORIZE_ALL_TRANSFERS); + testCapabilityActions(2, AUTHORIZE_NOMINATED_TRANSFERS); + testCapabilityActions(0, CHANGE_OR_DELETE_REFERENCES); + testCapabilityActions(1, CLOSE_FOLDERS); + testCapabilityActions(0, + CREATE_AND_ASSOCIATE_SELECTION_LISTS); + testCapabilityActions(0, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES); + testCapabilityActions(0, CREATE_MODIFY_DESTROY_EVENTS); + testCapabilityActions(0, + CREATE_MODIFY_DESTROY_FILEPLAN_METADATA); + testCapabilityActions(0, + CREATE_MODIFY_DESTROY_FILEPLAN_TYPES); + testCapabilityActions(0, CREATE_MODIFY_DESTROY_FOLDERS); + testCapabilityActions(0, + CREATE_MODIFY_DESTROY_RECORD_TYPES); + testCapabilityActions(0, + CREATE_MODIFY_DESTROY_REFERENCE_TYPES); + testCapabilityActions(0, CREATE_MODIFY_DESTROY_ROLES); + testCapabilityActions(0, + CREATE_MODIFY_DESTROY_TIMEFRAMES); + testCapabilityActions(0, + CREATE_MODIFY_DESTROY_USERS_AND_GROUPS); + testCapabilityActions(0, + CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS); + testCapabilityActions(1, CYCLE_VITAL_RECORDS); + testCapabilityActions(0, DECLARE_AUDIT_AS_RECORD); + testCapabilityActions(2, DECLARE_RECORDS); + testCapabilityActions(1, + DECLARE_RECORDS_IN_CLOSED_FOLDERS); + testCapabilityActions(0, DELETE_AUDIT); + testCapabilityActions(0, DELETE_LINKS); + testCapabilityActions(0, DELETE_RECORDS); + testCapabilityActions(0, DESTROY_RECORDS); + testCapabilityActions(1, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION); + testCapabilityActions(0, DISPLAY_RIGHTS_REPORT); + testCapabilityActions(0, EDIT_DECLARED_RECORD_METADATA); + testCapabilityActions(0, EDIT_NON_RECORD_METADATA); + testCapabilityActions(0, EDIT_RECORD_METADATA); + testCapabilityActions(0, EDIT_SELECTION_LISTS); + testCapabilityActions(0, ENABLE_DISABLE_AUDIT_BY_TYPES); + testCapabilityActions(0, EXPORT_AUDIT); + testCapabilityActions(1, + EXTEND_RETENTION_PERIOD_OR_FREEZE); + testCapabilityActions(1, FILE_RECORDS); + testCapabilityActions(0, + MAKE_OPTIONAL_PARAMETERS_MANDATORY); + testCapabilityActions(0, MANAGE_ACCESS_CONTROLS); + testCapabilityActions(0, MANAGE_ACCESS_RIGHTS); + testCapabilityActions(1, + MANUALLY_CHANGE_DISPOSITION_DATES); + testCapabilityActions(0, + MAP_CLASSIFICATION_GUIDE_METADATA); + testCapabilityActions(0, MAP_EMAIL_METADATA); + testCapabilityActions(0, MOVE_RECORDS); + testCapabilityActions(0, PASSWORD_CONTROL); + testCapabilityActions(1, PLANNING_REVIEW_CYCLES); + testCapabilityActions(1, RE_OPEN_FOLDERS); + testCapabilityActions(0, SELECT_AUDIT_METADATA); + testCapabilityActions(0, TRIGGER_AN_EVENT); + testCapabilityActions(1, UNDECLARE_RECORDS); + testCapabilityActions(2, UNFREEZE); + testCapabilityActions(0, UPDATE_CLASSIFICATION_DATES); + testCapabilityActions(0, UPDATE_EXEMPTION_CATEGORIES); + testCapabilityActions(0, UPDATE_TRIGGER_DATES); + testCapabilityActions(0, + UPDATE_VITAL_RECORD_CYCLE_INFORMATION); + testCapabilityActions(0, + UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS); + testCapabilityActions(0, VIEW_RECORDS); + testCapabilityActions(1, VIEW_UPDATE_REASONS_FOR_FREEZE); + + return null; + } + }, false, true); + } + + /** + * Test the capability actions + * + * @param count + * @param capability + */ + private void testCapabilityActions(int count, String capability) + { + assertEquals(count, securityService.getCapability(capability) + .getActionNames().size()); + } + + /** + * Test file plan as system + */ + public void testFilePlanAsSystem() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + // As system user + AuthenticationUtil + .setFullyAuthenticatedUser(AuthenticationUtil + .getSystemUserName()); + + Map access = securityService + .getCapabilities(filePlan); + assertEquals(66, access.size()); + check(access, ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, ADD_MODIFY_EVENT_DATES, + AccessStatus.DENIED); + check(access, APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + AccessStatus.DENIED); + check(access, ATTACH_RULES_TO_METADATA_PROPERTIES, + AccessStatus.ALLOWED); + check(access, AUTHORIZE_ALL_TRANSFERS, + AccessStatus.DENIED); + check(access, AUTHORIZE_NOMINATED_TRANSFERS, + AccessStatus.DENIED); + check(access, CHANGE_OR_DELETE_REFERENCES, + AccessStatus.UNDETERMINED); + check(access, CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, CREATE_AND_ASSOCIATE_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_EVENTS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FOLDERS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_RECORD_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_ROLES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_TIMEFRAMES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + AccessStatus.DENIED); + check(access, CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_AUDIT_AS_RECORD, + AccessStatus.ALLOWED); + check(access, DECLARE_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_RECORDS_IN_CLOSED_FOLDERS, + AccessStatus.DENIED); + check(access, DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, DELETE_RECORDS, AccessStatus.DENIED); + check(access, DESTROY_RECORDS, AccessStatus.DENIED); + check(access, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + AccessStatus.DENIED); + check(access, DISPLAY_RIGHTS_REPORT, + AccessStatus.ALLOWED); + check(access, EDIT_DECLARED_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_NON_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, EDIT_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, ENABLE_DISABLE_AUDIT_BY_TYPES, + AccessStatus.ALLOWED); + check(access, EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, EXTEND_RETENTION_PERIOD_OR_FREEZE, + AccessStatus.DENIED); + check(access, MAKE_OPTIONAL_PARAMETERS_MANDATORY, + AccessStatus.ALLOWED); + check(access, MANAGE_ACCESS_CONTROLS, + AccessStatus.ALLOWED); + check(access, MANAGE_ACCESS_RIGHTS, + AccessStatus.ALLOWED); + check(access, MANUALLY_CHANGE_DISPOSITION_DATES, + AccessStatus.DENIED); + check(access, MAP_CLASSIFICATION_GUIDE_METADATA, + AccessStatus.ALLOWED); + check(access, MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, PLANNING_REVIEW_CYCLES, + AccessStatus.DENIED); + check(access, RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, SELECT_AUDIT_METADATA, + AccessStatus.ALLOWED); + check(access, TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, UNFREEZE, AccessStatus.DENIED); + check(access, UPDATE_CLASSIFICATION_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_EXEMPTION_CATEGORIES, + AccessStatus.ALLOWED); + check(access, UPDATE_TRIGGER_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + AccessStatus.ALLOWED); + check(access, UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + AccessStatus.ALLOWED); + check(access, VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, VIEW_UPDATE_REASONS_FOR_FREEZE, + AccessStatus.DENIED); + + return null; + } + }, false, true); + } + + public void testFilePlanAsAdmin() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil + .setFullyAuthenticatedUser(AuthenticationUtil + .getAdminUserName()); + Map access = securityService + .getCapabilities(filePlan); + assertEquals(66, access.size()); + check(access, ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, ADD_MODIFY_EVENT_DATES, + AccessStatus.DENIED); + check(access, APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + AccessStatus.DENIED); + check(access, ATTACH_RULES_TO_METADATA_PROPERTIES, + AccessStatus.ALLOWED); + check(access, AUTHORIZE_ALL_TRANSFERS, + AccessStatus.DENIED); + check(access, AUTHORIZE_NOMINATED_TRANSFERS, + AccessStatus.DENIED); + check(access, CHANGE_OR_DELETE_REFERENCES, + AccessStatus.UNDETERMINED); + check(access, CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, CREATE_AND_ASSOCIATE_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_EVENTS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FOLDERS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_RECORD_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_ROLES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_TIMEFRAMES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + AccessStatus.DENIED); + check(access, CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_AUDIT_AS_RECORD, + AccessStatus.ALLOWED); + check(access, DECLARE_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_RECORDS_IN_CLOSED_FOLDERS, + AccessStatus.DENIED); + check(access, DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, DELETE_RECORDS, AccessStatus.DENIED); + check(access, DESTROY_RECORDS, AccessStatus.DENIED); + check(access, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + AccessStatus.DENIED); + check(access, DISPLAY_RIGHTS_REPORT, + AccessStatus.ALLOWED); + check(access, EDIT_DECLARED_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_NON_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, EDIT_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, ENABLE_DISABLE_AUDIT_BY_TYPES, + AccessStatus.ALLOWED); + check(access, EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, EXTEND_RETENTION_PERIOD_OR_FREEZE, + AccessStatus.DENIED); + check(access, MAKE_OPTIONAL_PARAMETERS_MANDATORY, + AccessStatus.ALLOWED); + check(access, MANAGE_ACCESS_CONTROLS, + AccessStatus.ALLOWED); + check(access, MANAGE_ACCESS_RIGHTS, + AccessStatus.ALLOWED); + check(access, MANUALLY_CHANGE_DISPOSITION_DATES, + AccessStatus.DENIED); + check(access, MAP_CLASSIFICATION_GUIDE_METADATA, + AccessStatus.ALLOWED); + check(access, MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, PLANNING_REVIEW_CYCLES, + AccessStatus.DENIED); + check(access, RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, SELECT_AUDIT_METADATA, + AccessStatus.ALLOWED); + check(access, TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, UNFREEZE, AccessStatus.DENIED); + check(access, UPDATE_CLASSIFICATION_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_EXEMPTION_CATEGORIES, + AccessStatus.ALLOWED); + check(access, UPDATE_TRIGGER_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + AccessStatus.ALLOWED); + check(access, UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + AccessStatus.ALLOWED); + check(access, VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, VIEW_UPDATE_REASONS_FOR_FREEZE, + AccessStatus.DENIED); + + return null; + } + }, false, true); + } + + /** + * Test file plan as administrator + */ + public void testFilePlanAsAdministrator() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil + .setFullyAuthenticatedUser(rmAdminName); + Map access = securityService + .getCapabilities(filePlan); + assertEquals(66, access.size()); + check(access, ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, ADD_MODIFY_EVENT_DATES, + AccessStatus.DENIED); + check(access, APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + AccessStatus.DENIED); + check(access, ATTACH_RULES_TO_METADATA_PROPERTIES, + AccessStatus.ALLOWED); + check(access, AUTHORIZE_ALL_TRANSFERS, + AccessStatus.DENIED); + check(access, AUTHORIZE_NOMINATED_TRANSFERS, + AccessStatus.DENIED); + check(access, CHANGE_OR_DELETE_REFERENCES, + AccessStatus.UNDETERMINED); + check(access, CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, CREATE_AND_ASSOCIATE_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_EVENTS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FOLDERS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_RECORD_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_ROLES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_TIMEFRAMES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + AccessStatus.DENIED); + check(access, CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_AUDIT_AS_RECORD, + AccessStatus.ALLOWED); + check(access, DECLARE_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_RECORDS_IN_CLOSED_FOLDERS, + AccessStatus.DENIED); + check(access, DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, DELETE_RECORDS, AccessStatus.DENIED); + check(access, DESTROY_RECORDS, AccessStatus.DENIED); + check(access, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + AccessStatus.DENIED); + check(access, DISPLAY_RIGHTS_REPORT, + AccessStatus.ALLOWED); + check(access, EDIT_DECLARED_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_NON_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, EDIT_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, ENABLE_DISABLE_AUDIT_BY_TYPES, + AccessStatus.ALLOWED); + check(access, EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, EXTEND_RETENTION_PERIOD_OR_FREEZE, + AccessStatus.DENIED); + check(access, MAKE_OPTIONAL_PARAMETERS_MANDATORY, + AccessStatus.ALLOWED); + check(access, MANAGE_ACCESS_CONTROLS, + AccessStatus.ALLOWED); + check(access, MANAGE_ACCESS_RIGHTS, + AccessStatus.ALLOWED); + check(access, MANUALLY_CHANGE_DISPOSITION_DATES, + AccessStatus.DENIED); + check(access, MAP_CLASSIFICATION_GUIDE_METADATA, + AccessStatus.ALLOWED); + check(access, MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, PLANNING_REVIEW_CYCLES, + AccessStatus.DENIED); + check(access, RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, SELECT_AUDIT_METADATA, + AccessStatus.ALLOWED); + check(access, TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, UNFREEZE, AccessStatus.DENIED); + check(access, UPDATE_CLASSIFICATION_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_EXEMPTION_CATEGORIES, + AccessStatus.ALLOWED); + check(access, UPDATE_TRIGGER_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + AccessStatus.ALLOWED); + check(access, UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + AccessStatus.ALLOWED); + check(access, VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, VIEW_UPDATE_REASONS_FOR_FREEZE, + AccessStatus.DENIED); + + return null; + } + }, false, true); + } + + public void testFilePlanAsRecordsManager() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + Set permissions = permissionService + .getAllSetPermissions(filePlan); + for (AccessPermission ap : permissions) + { + System.out.println(ap.getAuthority() + " -> " + + ap.getPermission() + " (" + + ap.getPosition() + ")"); + } + + AuthenticationUtil + .setFullyAuthenticatedUser(recordsManagerName); + Map access = securityService + .getCapabilities(filePlan); + assertEquals(66, access.size()); // 58 + File + check(access, ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, ADD_MODIFY_EVENT_DATES, + AccessStatus.DENIED); + check(access, APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + AccessStatus.DENIED); + check(access, ATTACH_RULES_TO_METADATA_PROPERTIES, + AccessStatus.ALLOWED); + check(access, AUTHORIZE_ALL_TRANSFERS, + AccessStatus.DENIED); + check(access, AUTHORIZE_NOMINATED_TRANSFERS, + AccessStatus.DENIED); + check(access, CHANGE_OR_DELETE_REFERENCES, + AccessStatus.DENIED); + check(access, CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, CREATE_AND_ASSOCIATE_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_EVENTS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FOLDERS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_RECORD_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_ROLES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_TIMEFRAMES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + AccessStatus.DENIED); + check(access, CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_AUDIT_AS_RECORD, + AccessStatus.ALLOWED); + check(access, DECLARE_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_RECORDS_IN_CLOSED_FOLDERS, + AccessStatus.DENIED); + check(access, DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, DELETE_RECORDS, AccessStatus.DENIED); + check(access, DESTROY_RECORDS, AccessStatus.DENIED); + check(access, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + AccessStatus.DENIED); + check(access, DISPLAY_RIGHTS_REPORT, + AccessStatus.ALLOWED); + check(access, EDIT_DECLARED_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_NON_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, EDIT_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, ENABLE_DISABLE_AUDIT_BY_TYPES, + AccessStatus.ALLOWED); + check(access, EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, EXTEND_RETENTION_PERIOD_OR_FREEZE, + AccessStatus.DENIED); + check(access, MAKE_OPTIONAL_PARAMETERS_MANDATORY, + AccessStatus.ALLOWED); + check(access, MANAGE_ACCESS_CONTROLS, + AccessStatus.DENIED); + check(access, MANAGE_ACCESS_RIGHTS, + AccessStatus.ALLOWED); + check(access, MANUALLY_CHANGE_DISPOSITION_DATES, + AccessStatus.DENIED); + check(access, MAP_CLASSIFICATION_GUIDE_METADATA, + AccessStatus.ALLOWED); + check(access, MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, PLANNING_REVIEW_CYCLES, + AccessStatus.DENIED); + check(access, RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, SELECT_AUDIT_METADATA, + AccessStatus.ALLOWED); + check(access, TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, UNFREEZE, AccessStatus.DENIED); + check(access, UPDATE_CLASSIFICATION_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_EXEMPTION_CATEGORIES, + AccessStatus.ALLOWED); + check(access, UPDATE_TRIGGER_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + AccessStatus.ALLOWED); + check(access, UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + AccessStatus.ALLOWED); + check(access, VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, VIEW_UPDATE_REASONS_FOR_FREEZE, + AccessStatus.DENIED); + + return null; + } + }, false, true); + } + + public void testFilePlanAsSecurityOfficer() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + List temp = new ArrayList(); + temp.add("ACCESS_AUDIT"); + capabilityService.getCapabilitiesAccessState(filePlan, temp); + + + AuthenticationUtil + .setFullyAuthenticatedUser(securityOfficerName); + Map access = securityService + .getCapabilities(filePlan); + assertEquals(66, access.size()); // 58 + File + check(access, ACCESS_AUDIT, AccessStatus.DENIED); + check(access, ADD_MODIFY_EVENT_DATES, + AccessStatus.DENIED); + check(access, APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + AccessStatus.DENIED); + check(access, ATTACH_RULES_TO_METADATA_PROPERTIES, + AccessStatus.DENIED); + check(access, AUTHORIZE_ALL_TRANSFERS, + AccessStatus.DENIED); + check(access, AUTHORIZE_NOMINATED_TRANSFERS, + AccessStatus.DENIED); + check(access, CHANGE_OR_DELETE_REFERENCES, + AccessStatus.DENIED); + check(access, CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, CREATE_AND_ASSOCIATE_SELECTION_LISTS, + AccessStatus.DENIED); + check(access, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_EVENTS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FOLDERS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_RECORD_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_ROLES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_TIMEFRAMES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + AccessStatus.DENIED); + check(access, CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_AUDIT_AS_RECORD, + AccessStatus.DENIED); + check(access, DECLARE_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_RECORDS_IN_CLOSED_FOLDERS, + AccessStatus.DENIED); + check(access, DELETE_AUDIT, AccessStatus.DENIED); + check(access, DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, DELETE_RECORDS, AccessStatus.DENIED); + check(access, DESTROY_RECORDS, AccessStatus.DENIED); + check(access, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + AccessStatus.DENIED); + check(access, DISPLAY_RIGHTS_REPORT, + AccessStatus.DENIED); + check(access, EDIT_DECLARED_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_NON_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, EDIT_SELECTION_LISTS, AccessStatus.DENIED); + check(access, ENABLE_DISABLE_AUDIT_BY_TYPES, + AccessStatus.DENIED); + check(access, EXPORT_AUDIT, AccessStatus.DENIED); + check(access, EXTEND_RETENTION_PERIOD_OR_FREEZE, + AccessStatus.DENIED); + check(access, MAKE_OPTIONAL_PARAMETERS_MANDATORY, + AccessStatus.DENIED); + check(access, MANAGE_ACCESS_CONTROLS, + AccessStatus.DENIED); + check(access, MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + check(access, MANUALLY_CHANGE_DISPOSITION_DATES, + AccessStatus.DENIED); + check(access, MAP_CLASSIFICATION_GUIDE_METADATA, + AccessStatus.DENIED); + check(access, MAP_EMAIL_METADATA, AccessStatus.DENIED); + check(access, MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, PASSWORD_CONTROL, AccessStatus.DENIED); + check(access, PLANNING_REVIEW_CYCLES, + AccessStatus.DENIED); + check(access, RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, SELECT_AUDIT_METADATA, + AccessStatus.DENIED); + check(access, TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, UNFREEZE, AccessStatus.DENIED); + check(access, UPDATE_CLASSIFICATION_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_EXEMPTION_CATEGORIES, + AccessStatus.ALLOWED); + check(access, UPDATE_TRIGGER_DATES, AccessStatus.DENIED); + check(access, UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + AccessStatus.DENIED); + check(access, UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + AccessStatus.ALLOWED); + check(access, VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, VIEW_UPDATE_REASONS_FOR_FREEZE, + AccessStatus.DENIED); + + return null; + } + }, false, true); + } + + /** + * Test file plan as power user + */ + public void testFilePlanAsPowerUser() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil + .setFullyAuthenticatedUser(powerUserName); + Map access = securityService + .getCapabilities(filePlan); + assertEquals(66, access.size()); // 58 + File + check(access, ACCESS_AUDIT, AccessStatus.DENIED); + check(access, ADD_MODIFY_EVENT_DATES, + AccessStatus.DENIED); + check(access, APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + AccessStatus.DENIED); + check(access, ATTACH_RULES_TO_METADATA_PROPERTIES, + AccessStatus.DENIED); + check(access, AUTHORIZE_ALL_TRANSFERS, + AccessStatus.DENIED); + check(access, AUTHORIZE_NOMINATED_TRANSFERS, + AccessStatus.DENIED); + check(access, CHANGE_OR_DELETE_REFERENCES, + AccessStatus.DENIED); + check(access, CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, CREATE_AND_ASSOCIATE_SELECTION_LISTS, + AccessStatus.DENIED); + check(access, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_EVENTS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FOLDERS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_RECORD_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_ROLES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_TIMEFRAMES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + AccessStatus.DENIED); + check(access, CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_AUDIT_AS_RECORD, + AccessStatus.DENIED); + check(access, DECLARE_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_RECORDS_IN_CLOSED_FOLDERS, + AccessStatus.DENIED); + check(access, DELETE_AUDIT, AccessStatus.DENIED); + check(access, DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, DELETE_RECORDS, AccessStatus.DENIED); + check(access, DESTROY_RECORDS, AccessStatus.DENIED); + check(access, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + AccessStatus.DENIED); + check(access, DISPLAY_RIGHTS_REPORT, + AccessStatus.DENIED); + check(access, EDIT_DECLARED_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_NON_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, EDIT_SELECTION_LISTS, AccessStatus.DENIED); + check(access, ENABLE_DISABLE_AUDIT_BY_TYPES, + AccessStatus.DENIED); + check(access, EXPORT_AUDIT, AccessStatus.DENIED); + check(access, EXTEND_RETENTION_PERIOD_OR_FREEZE, + AccessStatus.DENIED); + check(access, MAKE_OPTIONAL_PARAMETERS_MANDATORY, + AccessStatus.DENIED); + check(access, MANAGE_ACCESS_CONTROLS, + AccessStatus.DENIED); + check(access, MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + check(access, MANUALLY_CHANGE_DISPOSITION_DATES, + AccessStatus.DENIED); + check(access, MAP_CLASSIFICATION_GUIDE_METADATA, + AccessStatus.DENIED); + check(access, MAP_EMAIL_METADATA, AccessStatus.DENIED); + check(access, MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, PASSWORD_CONTROL, AccessStatus.DENIED); + check(access, PLANNING_REVIEW_CYCLES, + AccessStatus.DENIED); + check(access, RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, SELECT_AUDIT_METADATA, + AccessStatus.DENIED); + check(access, TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, UNFREEZE, AccessStatus.DENIED); + check(access, UPDATE_CLASSIFICATION_DATES, + AccessStatus.DENIED); + check(access, UPDATE_EXEMPTION_CATEGORIES, + AccessStatus.DENIED); + check(access, UPDATE_TRIGGER_DATES, AccessStatus.DENIED); + check(access, UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + AccessStatus.DENIED); + check(access, UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + AccessStatus.DENIED); + check(access, VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, VIEW_UPDATE_REASONS_FOR_FREEZE, + AccessStatus.DENIED); + + return null; + } + }, false, true); + } + + /** + * Test file plan as user + */ + public void testFilePlanAsUser() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil + .setFullyAuthenticatedUser(rmUserName); + Map access = securityService + .getCapabilities(filePlan); + assertEquals(66, access.size()); // 58 + File + check(access, ACCESS_AUDIT, AccessStatus.DENIED); + check(access, ADD_MODIFY_EVENT_DATES, + AccessStatus.DENIED); + check(access, APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + AccessStatus.DENIED); + check(access, ATTACH_RULES_TO_METADATA_PROPERTIES, + AccessStatus.DENIED); + check(access, AUTHORIZE_ALL_TRANSFERS, + AccessStatus.DENIED); + check(access, AUTHORIZE_NOMINATED_TRANSFERS, + AccessStatus.DENIED); + check(access, CHANGE_OR_DELETE_REFERENCES, + AccessStatus.DENIED); + check(access, CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, CREATE_AND_ASSOCIATE_SELECTION_LISTS, + AccessStatus.DENIED); + check(access, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_EVENTS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FOLDERS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_RECORD_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_ROLES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_TIMEFRAMES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + AccessStatus.DENIED); + check(access, CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_AUDIT_AS_RECORD, + AccessStatus.DENIED); + check(access, DECLARE_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_RECORDS_IN_CLOSED_FOLDERS, + AccessStatus.DENIED); + check(access, DELETE_AUDIT, AccessStatus.DENIED); + check(access, DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, DELETE_RECORDS, AccessStatus.DENIED); + check(access, DESTROY_RECORDS, AccessStatus.DENIED); + check(access, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + AccessStatus.DENIED); + check(access, DISPLAY_RIGHTS_REPORT, + AccessStatus.DENIED); + check(access, EDIT_DECLARED_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_NON_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, EDIT_SELECTION_LISTS, AccessStatus.DENIED); + check(access, ENABLE_DISABLE_AUDIT_BY_TYPES, + AccessStatus.DENIED); + check(access, EXPORT_AUDIT, AccessStatus.DENIED); + check(access, EXTEND_RETENTION_PERIOD_OR_FREEZE, + AccessStatus.DENIED); + check(access, MAKE_OPTIONAL_PARAMETERS_MANDATORY, + AccessStatus.DENIED); + check(access, MANAGE_ACCESS_CONTROLS, + AccessStatus.DENIED); + check(access, MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + check(access, MANUALLY_CHANGE_DISPOSITION_DATES, + AccessStatus.DENIED); + check(access, MAP_CLASSIFICATION_GUIDE_METADATA, + AccessStatus.DENIED); + check(access, MAP_EMAIL_METADATA, AccessStatus.DENIED); + check(access, MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, PASSWORD_CONTROL, AccessStatus.DENIED); + check(access, PLANNING_REVIEW_CYCLES, + AccessStatus.DENIED); + check(access, RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, SELECT_AUDIT_METADATA, + AccessStatus.DENIED); + check(access, TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, UNFREEZE, AccessStatus.DENIED); + check(access, UPDATE_CLASSIFICATION_DATES, + AccessStatus.DENIED); + check(access, UPDATE_EXEMPTION_CATEGORIES, + AccessStatus.DENIED); + check(access, UPDATE_TRIGGER_DATES, AccessStatus.DENIED); + check(access, UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + AccessStatus.DENIED); + check(access, UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + AccessStatus.DENIED); + check(access, VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, VIEW_UPDATE_REASONS_FOR_FREEZE, + AccessStatus.DENIED); + + return null; + } + }, false, true); + } + + /** + * Test record category as system + */ + public void testRecordCategoryAsSystem() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil + .setFullyAuthenticatedUser(AuthenticationUtil.SYSTEM_USER_NAME); + + Map access = securityService + .getCapabilities(rmContainer); + assertEquals(66, access.size()); + check(access, ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, ADD_MODIFY_EVENT_DATES, + AccessStatus.DENIED); + check(access, APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + AccessStatus.DENIED); + check(access, ATTACH_RULES_TO_METADATA_PROPERTIES, + AccessStatus.ALLOWED); + check(access, AUTHORIZE_ALL_TRANSFERS, + AccessStatus.DENIED); + check(access, AUTHORIZE_NOMINATED_TRANSFERS, + AccessStatus.DENIED); + check(access, CHANGE_OR_DELETE_REFERENCES, + AccessStatus.UNDETERMINED); + check(access, CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, CREATE_AND_ASSOCIATE_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_EVENTS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FOLDERS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_RECORD_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_ROLES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_TIMEFRAMES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + AccessStatus.DENIED); + check(access, CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_AUDIT_AS_RECORD, + AccessStatus.ALLOWED); + check(access, DECLARE_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_RECORDS_IN_CLOSED_FOLDERS, + AccessStatus.DENIED); + check(access, DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, DELETE_RECORDS, AccessStatus.DENIED); + check(access, DESTROY_RECORDS, AccessStatus.DENIED); + check(access, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + AccessStatus.DENIED); + check(access, DISPLAY_RIGHTS_REPORT, + AccessStatus.ALLOWED); + check(access, EDIT_DECLARED_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_NON_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, EDIT_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, ENABLE_DISABLE_AUDIT_BY_TYPES, + AccessStatus.ALLOWED); + check(access, EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, EXTEND_RETENTION_PERIOD_OR_FREEZE, + AccessStatus.DENIED); + check(access, MAKE_OPTIONAL_PARAMETERS_MANDATORY, + AccessStatus.ALLOWED); + check(access, MANAGE_ACCESS_CONTROLS, + AccessStatus.ALLOWED); + check(access, MANAGE_ACCESS_RIGHTS, + AccessStatus.ALLOWED); + check(access, MANUALLY_CHANGE_DISPOSITION_DATES, + AccessStatus.DENIED); + check(access, MAP_CLASSIFICATION_GUIDE_METADATA, + AccessStatus.ALLOWED); + check(access, MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, PLANNING_REVIEW_CYCLES, + AccessStatus.DENIED); + check(access, RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, SELECT_AUDIT_METADATA, + AccessStatus.ALLOWED); + check(access, TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, UNFREEZE, AccessStatus.DENIED); + check(access, UPDATE_CLASSIFICATION_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_EXEMPTION_CATEGORIES, + AccessStatus.ALLOWED); + check(access, UPDATE_TRIGGER_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + AccessStatus.ALLOWED); + check(access, UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + AccessStatus.ALLOWED); + check(access, VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, VIEW_UPDATE_REASONS_FOR_FREEZE, + AccessStatus.DENIED); + + return null; + } + }, false, true); + } + + /** + * Test record category as admin + */ + public void testRecordCategoryAsAdmin() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil + .setFullyAuthenticatedUser(AuthenticationUtil + .getAdminUserName()); + Map access = securityService + .getCapabilities(rmContainer); + assertEquals(66, access.size()); + check(access, ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, ADD_MODIFY_EVENT_DATES, + AccessStatus.DENIED); + check(access, APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + AccessStatus.DENIED); + check(access, ATTACH_RULES_TO_METADATA_PROPERTIES, + AccessStatus.ALLOWED); + check(access, AUTHORIZE_ALL_TRANSFERS, + AccessStatus.DENIED); + check(access, AUTHORIZE_NOMINATED_TRANSFERS, + AccessStatus.DENIED); + check(access, CHANGE_OR_DELETE_REFERENCES, + AccessStatus.UNDETERMINED); + check(access, CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, CREATE_AND_ASSOCIATE_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_EVENTS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FOLDERS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_RECORD_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_ROLES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_TIMEFRAMES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + AccessStatus.DENIED); + check(access, CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_AUDIT_AS_RECORD, + AccessStatus.ALLOWED); + check(access, DECLARE_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_RECORDS_IN_CLOSED_FOLDERS, + AccessStatus.DENIED); + check(access, DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, DELETE_RECORDS, AccessStatus.DENIED); + check(access, DESTROY_RECORDS, AccessStatus.DENIED); + check(access, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + AccessStatus.DENIED); + check(access, DISPLAY_RIGHTS_REPORT, + AccessStatus.ALLOWED); + check(access, EDIT_DECLARED_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_NON_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, EDIT_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, ENABLE_DISABLE_AUDIT_BY_TYPES, + AccessStatus.ALLOWED); + check(access, EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, EXTEND_RETENTION_PERIOD_OR_FREEZE, + AccessStatus.DENIED); + check(access, MAKE_OPTIONAL_PARAMETERS_MANDATORY, + AccessStatus.ALLOWED); + check(access, MANAGE_ACCESS_CONTROLS, + AccessStatus.ALLOWED); + check(access, MANAGE_ACCESS_RIGHTS, + AccessStatus.ALLOWED); + check(access, MANUALLY_CHANGE_DISPOSITION_DATES, + AccessStatus.DENIED); + check(access, MAP_CLASSIFICATION_GUIDE_METADATA, + AccessStatus.ALLOWED); + check(access, MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, PLANNING_REVIEW_CYCLES, + AccessStatus.DENIED); + check(access, RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, SELECT_AUDIT_METADATA, + AccessStatus.ALLOWED); + check(access, TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, UNFREEZE, AccessStatus.DENIED); + check(access, UPDATE_CLASSIFICATION_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_EXEMPTION_CATEGORIES, + AccessStatus.ALLOWED); + check(access, UPDATE_TRIGGER_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + AccessStatus.ALLOWED); + check(access, UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + AccessStatus.ALLOWED); + check(access, VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, VIEW_UPDATE_REASONS_FOR_FREEZE, + AccessStatus.DENIED); + + return null; + } + }, false, true); + } + + /** + * Test record category as administrator + */ + public void testRecordCategoryAsAdministrator() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil + .setFullyAuthenticatedUser(rmAdminName); + Map access = securityService + .getCapabilities(rmContainer); + assertEquals(66, access.size()); + check(access, ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, ADD_MODIFY_EVENT_DATES, + AccessStatus.DENIED); + check(access, APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + AccessStatus.DENIED); + check(access, ATTACH_RULES_TO_METADATA_PROPERTIES, + AccessStatus.ALLOWED); + check(access, AUTHORIZE_ALL_TRANSFERS, + AccessStatus.DENIED); + check(access, AUTHORIZE_NOMINATED_TRANSFERS, + AccessStatus.DENIED); + check(access, CHANGE_OR_DELETE_REFERENCES, + AccessStatus.UNDETERMINED); + check(access, CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, CREATE_AND_ASSOCIATE_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_EVENTS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FOLDERS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_RECORD_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_ROLES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_TIMEFRAMES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + AccessStatus.DENIED); + check(access, CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_AUDIT_AS_RECORD, + AccessStatus.ALLOWED); + check(access, DECLARE_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_RECORDS_IN_CLOSED_FOLDERS, + AccessStatus.DENIED); + check(access, DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, DELETE_RECORDS, AccessStatus.DENIED); + check(access, DESTROY_RECORDS, AccessStatus.DENIED); + check(access, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + AccessStatus.DENIED); + check(access, DISPLAY_RIGHTS_REPORT, + AccessStatus.ALLOWED); + check(access, EDIT_DECLARED_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_NON_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, EDIT_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, ENABLE_DISABLE_AUDIT_BY_TYPES, + AccessStatus.ALLOWED); + check(access, EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, EXTEND_RETENTION_PERIOD_OR_FREEZE, + AccessStatus.DENIED); + check(access, MAKE_OPTIONAL_PARAMETERS_MANDATORY, + AccessStatus.ALLOWED); + check(access, MANAGE_ACCESS_CONTROLS, + AccessStatus.ALLOWED); + check(access, MANAGE_ACCESS_RIGHTS, + AccessStatus.ALLOWED); + check(access, MANUALLY_CHANGE_DISPOSITION_DATES, + AccessStatus.DENIED); + check(access, MAP_CLASSIFICATION_GUIDE_METADATA, + AccessStatus.ALLOWED); + check(access, MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, PLANNING_REVIEW_CYCLES, + AccessStatus.DENIED); + check(access, RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, SELECT_AUDIT_METADATA, + AccessStatus.ALLOWED); + check(access, TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, UNFREEZE, AccessStatus.DENIED); + check(access, UPDATE_CLASSIFICATION_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_EXEMPTION_CATEGORIES, + AccessStatus.ALLOWED); + check(access, UPDATE_TRIGGER_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + AccessStatus.ALLOWED); + check(access, UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + AccessStatus.ALLOWED); + check(access, VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, VIEW_UPDATE_REASONS_FOR_FREEZE, + AccessStatus.DENIED); + + return null; + } + }, false, true); + } + + /** + * Test record category as records manager + */ + public void testRecordCategoryAsRecordsManager() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil + .setFullyAuthenticatedUser(recordsManagerName); + // permissionService.setPermission(recordCategory_1, + // rm_records_manager, FILING, true); + Map access = securityService + .getCapabilities(rmContainer); + assertEquals(66, access.size()); // 58 + File + check(access, ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, ADD_MODIFY_EVENT_DATES, + AccessStatus.DENIED); + check(access, APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + AccessStatus.DENIED); + check(access, ATTACH_RULES_TO_METADATA_PROPERTIES, + AccessStatus.ALLOWED); + check(access, AUTHORIZE_ALL_TRANSFERS, + AccessStatus.DENIED); + check(access, AUTHORIZE_NOMINATED_TRANSFERS, + AccessStatus.DENIED); + check(access, CHANGE_OR_DELETE_REFERENCES, + AccessStatus.UNDETERMINED); + check(access, CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, CREATE_AND_ASSOCIATE_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_EVENTS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FOLDERS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_RECORD_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_ROLES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_TIMEFRAMES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + AccessStatus.DENIED); + check(access, CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_AUDIT_AS_RECORD, + AccessStatus.ALLOWED); + check(access, DECLARE_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_RECORDS_IN_CLOSED_FOLDERS, + AccessStatus.DENIED); + check(access, DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, DELETE_RECORDS, AccessStatus.DENIED); + check(access, DESTROY_RECORDS, AccessStatus.DENIED); + check(access, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + AccessStatus.DENIED); + check(access, DISPLAY_RIGHTS_REPORT, + AccessStatus.ALLOWED); + check(access, EDIT_DECLARED_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_NON_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, EDIT_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, ENABLE_DISABLE_AUDIT_BY_TYPES, + AccessStatus.ALLOWED); + check(access, EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, EXTEND_RETENTION_PERIOD_OR_FREEZE, + AccessStatus.DENIED); + check(access, MAKE_OPTIONAL_PARAMETERS_MANDATORY, + AccessStatus.ALLOWED); + check(access, MANAGE_ACCESS_CONTROLS, + AccessStatus.DENIED); + check(access, MANAGE_ACCESS_RIGHTS, + AccessStatus.ALLOWED); + check(access, MANUALLY_CHANGE_DISPOSITION_DATES, + AccessStatus.DENIED); + check(access, MAP_CLASSIFICATION_GUIDE_METADATA, + AccessStatus.ALLOWED); + check(access, MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, PLANNING_REVIEW_CYCLES, + AccessStatus.DENIED); + check(access, RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, SELECT_AUDIT_METADATA, + AccessStatus.ALLOWED); + check(access, TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, UNFREEZE, AccessStatus.DENIED); + check(access, UPDATE_CLASSIFICATION_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_EXEMPTION_CATEGORIES, + AccessStatus.ALLOWED); + check(access, UPDATE_TRIGGER_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + AccessStatus.ALLOWED); + check(access, UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + AccessStatus.ALLOWED); + check(access, VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, VIEW_UPDATE_REASONS_FOR_FREEZE, + AccessStatus.DENIED); + + return null; + } + }, false, true); + } + + /** + * Test record category as security officer + */ + public void testRecordCategoryAsSecurityOfficer() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil + .setFullyAuthenticatedUser(securityOfficerName); + // permissionService.setPermission(recordCategory_1, + // securityOfficerName, FILING, true); + Map access = securityService + .getCapabilities(rmContainer); + assertEquals(66, access.size()); // 58 + File + check(access, ACCESS_AUDIT, AccessStatus.DENIED); + check(access, ADD_MODIFY_EVENT_DATES, + AccessStatus.DENIED); + check(access, APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + AccessStatus.DENIED); + check(access, ATTACH_RULES_TO_METADATA_PROPERTIES, + AccessStatus.DENIED); + check(access, AUTHORIZE_ALL_TRANSFERS, + AccessStatus.DENIED); + check(access, AUTHORIZE_NOMINATED_TRANSFERS, + AccessStatus.DENIED); + check(access, CHANGE_OR_DELETE_REFERENCES, + AccessStatus.DENIED); + check(access, CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, CREATE_AND_ASSOCIATE_SELECTION_LISTS, + AccessStatus.DENIED); + check(access, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_EVENTS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FOLDERS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_RECORD_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_ROLES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_TIMEFRAMES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + AccessStatus.DENIED); + check(access, CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_AUDIT_AS_RECORD, + AccessStatus.DENIED); + check(access, DECLARE_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_RECORDS_IN_CLOSED_FOLDERS, + AccessStatus.DENIED); + check(access, DELETE_AUDIT, AccessStatus.DENIED); + check(access, DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, DELETE_RECORDS, AccessStatus.DENIED); + check(access, DESTROY_RECORDS, AccessStatus.DENIED); + check(access, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + AccessStatus.DENIED); + check(access, DISPLAY_RIGHTS_REPORT, + AccessStatus.DENIED); + check(access, EDIT_DECLARED_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_NON_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, EDIT_SELECTION_LISTS, AccessStatus.DENIED); + check(access, ENABLE_DISABLE_AUDIT_BY_TYPES, + AccessStatus.DENIED); + check(access, EXPORT_AUDIT, AccessStatus.DENIED); + check(access, EXTEND_RETENTION_PERIOD_OR_FREEZE, + AccessStatus.DENIED); + check(access, MAKE_OPTIONAL_PARAMETERS_MANDATORY, + AccessStatus.DENIED); + check(access, MANAGE_ACCESS_CONTROLS, + AccessStatus.DENIED); + check(access, MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + check(access, MANUALLY_CHANGE_DISPOSITION_DATES, + AccessStatus.DENIED); + check(access, MAP_CLASSIFICATION_GUIDE_METADATA, + AccessStatus.DENIED); + check(access, MAP_EMAIL_METADATA, AccessStatus.DENIED); + check(access, MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, PASSWORD_CONTROL, AccessStatus.DENIED); + check(access, PLANNING_REVIEW_CYCLES, + AccessStatus.DENIED); + check(access, RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, SELECT_AUDIT_METADATA, + AccessStatus.DENIED); + check(access, TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, UNFREEZE, AccessStatus.DENIED); + check(access, UPDATE_CLASSIFICATION_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_EXEMPTION_CATEGORIES, + AccessStatus.ALLOWED); + check(access, UPDATE_TRIGGER_DATES, AccessStatus.DENIED); + check(access, UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + AccessStatus.DENIED); + check(access, UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + AccessStatus.ALLOWED); + check(access, VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, VIEW_UPDATE_REASONS_FOR_FREEZE, + AccessStatus.DENIED); + + return null; + } + }, false, true); + } + + /** + * Test record category as power user + */ + public void testRecordCategoryAsPowerUser() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil + .setFullyAuthenticatedUser(powerUserName); + // permissionService.setPermission(rmContainer, + // powerUserName, FILING, true); + Map access = securityService + .getCapabilities(rmContainer); + assertEquals(66, access.size()); // 58 + File + check(access, ACCESS_AUDIT, AccessStatus.DENIED); + check(access, ADD_MODIFY_EVENT_DATES, + AccessStatus.DENIED); + check(access, APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + AccessStatus.DENIED); + check(access, ATTACH_RULES_TO_METADATA_PROPERTIES, + AccessStatus.DENIED); + check(access, AUTHORIZE_ALL_TRANSFERS, + AccessStatus.DENIED); + check(access, AUTHORIZE_NOMINATED_TRANSFERS, + AccessStatus.DENIED); + check(access, CHANGE_OR_DELETE_REFERENCES, + AccessStatus.DENIED); + check(access, CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, CREATE_AND_ASSOCIATE_SELECTION_LISTS, + AccessStatus.DENIED); + check(access, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_EVENTS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FOLDERS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_RECORD_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_ROLES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_TIMEFRAMES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + AccessStatus.DENIED); + check(access, CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_AUDIT_AS_RECORD, + AccessStatus.DENIED); + check(access, DECLARE_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_RECORDS_IN_CLOSED_FOLDERS, + AccessStatus.DENIED); + check(access, DELETE_AUDIT, AccessStatus.DENIED); + check(access, DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, DELETE_RECORDS, AccessStatus.DENIED); + check(access, DESTROY_RECORDS, AccessStatus.DENIED); + check(access, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + AccessStatus.DENIED); + check(access, DISPLAY_RIGHTS_REPORT, + AccessStatus.DENIED); + check(access, EDIT_DECLARED_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_NON_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, EDIT_SELECTION_LISTS, AccessStatus.DENIED); + check(access, ENABLE_DISABLE_AUDIT_BY_TYPES, + AccessStatus.DENIED); + check(access, EXPORT_AUDIT, AccessStatus.DENIED); + check(access, EXTEND_RETENTION_PERIOD_OR_FREEZE, + AccessStatus.DENIED); + check(access, MAKE_OPTIONAL_PARAMETERS_MANDATORY, + AccessStatus.DENIED); + check(access, MANAGE_ACCESS_CONTROLS, + AccessStatus.DENIED); + check(access, MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + check(access, MANUALLY_CHANGE_DISPOSITION_DATES, + AccessStatus.DENIED); + check(access, MAP_CLASSIFICATION_GUIDE_METADATA, + AccessStatus.DENIED); + check(access, MAP_EMAIL_METADATA, AccessStatus.DENIED); + check(access, MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, PASSWORD_CONTROL, AccessStatus.DENIED); + check(access, PLANNING_REVIEW_CYCLES, + AccessStatus.DENIED); + check(access, RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, SELECT_AUDIT_METADATA, + AccessStatus.DENIED); + check(access, TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, UNFREEZE, AccessStatus.DENIED); + check(access, UPDATE_CLASSIFICATION_DATES, + AccessStatus.DENIED); + check(access, UPDATE_EXEMPTION_CATEGORIES, + AccessStatus.DENIED); + check(access, UPDATE_TRIGGER_DATES, AccessStatus.DENIED); + check(access, UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + AccessStatus.DENIED); + check(access, UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + AccessStatus.DENIED); + check(access, VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, VIEW_UPDATE_REASONS_FOR_FREEZE, + AccessStatus.DENIED); + + return null; + } + }, false, true); + } + + /** + * Test record category as user + */ + public void testRecordCategoryAsUser() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil + .setFullyAuthenticatedUser(rmUserName); + // permissionService.setPermission(rmContainer, + // rmUserName, FILING, true); + Map access = securityService + .getCapabilities(rmContainer); + assertEquals(66, access.size()); // 58 + File + check(access, ACCESS_AUDIT, AccessStatus.DENIED); + check(access, ADD_MODIFY_EVENT_DATES, + AccessStatus.DENIED); + check(access, APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + AccessStatus.DENIED); + check(access, ATTACH_RULES_TO_METADATA_PROPERTIES, + AccessStatus.DENIED); + check(access, AUTHORIZE_ALL_TRANSFERS, + AccessStatus.DENIED); + check(access, AUTHORIZE_NOMINATED_TRANSFERS, + AccessStatus.DENIED); + check(access, CHANGE_OR_DELETE_REFERENCES, + AccessStatus.DENIED); + check(access, CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, CREATE_AND_ASSOCIATE_SELECTION_LISTS, + AccessStatus.DENIED); + check(access, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_EVENTS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FOLDERS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_RECORD_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_ROLES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_TIMEFRAMES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + AccessStatus.DENIED); + check(access, CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_AUDIT_AS_RECORD, + AccessStatus.DENIED); + check(access, DECLARE_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_RECORDS_IN_CLOSED_FOLDERS, + AccessStatus.DENIED); + check(access, DELETE_AUDIT, AccessStatus.DENIED); + check(access, DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, DELETE_RECORDS, AccessStatus.DENIED); + check(access, DESTROY_RECORDS, AccessStatus.DENIED); + check(access, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + AccessStatus.DENIED); + check(access, DISPLAY_RIGHTS_REPORT, + AccessStatus.DENIED); + check(access, EDIT_DECLARED_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_NON_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, EDIT_SELECTION_LISTS, AccessStatus.DENIED); + check(access, ENABLE_DISABLE_AUDIT_BY_TYPES, + AccessStatus.DENIED); + check(access, EXPORT_AUDIT, AccessStatus.DENIED); + check(access, EXTEND_RETENTION_PERIOD_OR_FREEZE, + AccessStatus.DENIED); + check(access, MAKE_OPTIONAL_PARAMETERS_MANDATORY, + AccessStatus.DENIED); + check(access, MANAGE_ACCESS_CONTROLS, + AccessStatus.DENIED); + check(access, MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + check(access, MANUALLY_CHANGE_DISPOSITION_DATES, + AccessStatus.DENIED); + check(access, MAP_CLASSIFICATION_GUIDE_METADATA, + AccessStatus.DENIED); + check(access, MAP_EMAIL_METADATA, AccessStatus.DENIED); + check(access, MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, PASSWORD_CONTROL, AccessStatus.DENIED); + check(access, PLANNING_REVIEW_CYCLES, + AccessStatus.DENIED); + check(access, RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, SELECT_AUDIT_METADATA, + AccessStatus.DENIED); + check(access, TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, UNFREEZE, AccessStatus.DENIED); + check(access, UPDATE_CLASSIFICATION_DATES, + AccessStatus.DENIED); + check(access, UPDATE_EXEMPTION_CATEGORIES, + AccessStatus.DENIED); + check(access, UPDATE_TRIGGER_DATES, AccessStatus.DENIED); + check(access, UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + AccessStatus.DENIED); + check(access, UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + AccessStatus.DENIED); + check(access, VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, VIEW_UPDATE_REASONS_FOR_FREEZE, + AccessStatus.DENIED); + + return null; + } + }, false, true); + } + + /** + * Test record folder as system + */ + public void testRecordFolderAsSystem() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil + .setFullyAuthenticatedUser(AuthenticationUtil.SYSTEM_USER_NAME); + + Map access = securityService + .getCapabilities(rmFolder); + assertEquals(66, access.size()); + check(access, ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, ADD_MODIFY_EVENT_DATES, + AccessStatus.ALLOWED); + check(access, APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + AccessStatus.DENIED); + check(access, ATTACH_RULES_TO_METADATA_PROPERTIES, + AccessStatus.ALLOWED); + check(access, AUTHORIZE_ALL_TRANSFERS, + AccessStatus.DENIED); + check(access, AUTHORIZE_NOMINATED_TRANSFERS, + AccessStatus.DENIED); + check(access, CHANGE_OR_DELETE_REFERENCES, + AccessStatus.UNDETERMINED); + check(access, CLOSE_FOLDERS, AccessStatus.ALLOWED); + check(access, CREATE_AND_ASSOCIATE_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_EVENTS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FOLDERS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_RECORD_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_ROLES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_TIMEFRAMES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + AccessStatus.DENIED); + check(access, CYCLE_VITAL_RECORDS, AccessStatus.DENIED); // rmFolder + // is + // not + // a + // vital + // record + check(access, DECLARE_AUDIT_AS_RECORD, + AccessStatus.ALLOWED); + check(access, DECLARE_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_RECORDS_IN_CLOSED_FOLDERS, + AccessStatus.DENIED); + check(access, DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, DELETE_RECORDS, AccessStatus.DENIED); + check(access, DESTROY_RECORDS, AccessStatus.ALLOWED); + check(access, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + AccessStatus.DENIED); + check(access, DISPLAY_RIGHTS_REPORT, + AccessStatus.ALLOWED); + check(access, EDIT_DECLARED_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_NON_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, EDIT_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, ENABLE_DISABLE_AUDIT_BY_TYPES, + AccessStatus.ALLOWED); + check(access, EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, EXTEND_RETENTION_PERIOD_OR_FREEZE, + AccessStatus.ALLOWED); + check(access, MAKE_OPTIONAL_PARAMETERS_MANDATORY, + AccessStatus.ALLOWED); + check(access, MANAGE_ACCESS_CONTROLS, + AccessStatus.ALLOWED); + check(access, MANAGE_ACCESS_RIGHTS, + AccessStatus.ALLOWED); + check(access, MANUALLY_CHANGE_DISPOSITION_DATES, + AccessStatus.ALLOWED); + check(access, MAP_CLASSIFICATION_GUIDE_METADATA, + AccessStatus.ALLOWED); + check(access, MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, PLANNING_REVIEW_CYCLES, + AccessStatus.DENIED); + check(access, RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, SELECT_AUDIT_METADATA, + AccessStatus.ALLOWED); + check(access, TRIGGER_AN_EVENT, AccessStatus.ALLOWED); + check(access, UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, UNFREEZE, AccessStatus.DENIED); + check(access, UPDATE_CLASSIFICATION_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_EXEMPTION_CATEGORIES, + AccessStatus.ALLOWED); + check(access, UPDATE_TRIGGER_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + AccessStatus.ALLOWED); + check(access, UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + AccessStatus.ALLOWED); + check(access, VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, VIEW_UPDATE_REASONS_FOR_FREEZE, + AccessStatus.DENIED); + + return null; + } + }, false, true); + + } + + /** + * Test record folder as admin + */ + public void testRecordFolderAsAdmin() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil + .setFullyAuthenticatedUser(AuthenticationUtil + .getAdminUserName()); + Map access = securityService + .getCapabilities(rmFolder); + assertEquals(66, access.size()); + check(access, ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, ADD_MODIFY_EVENT_DATES, + AccessStatus.ALLOWED); + check(access, APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + AccessStatus.DENIED); + check(access, ATTACH_RULES_TO_METADATA_PROPERTIES, + AccessStatus.ALLOWED); + check(access, AUTHORIZE_ALL_TRANSFERS, + AccessStatus.DENIED); + check(access, AUTHORIZE_NOMINATED_TRANSFERS, + AccessStatus.DENIED); + check(access, CHANGE_OR_DELETE_REFERENCES, + AccessStatus.UNDETERMINED); + check(access, CLOSE_FOLDERS, AccessStatus.ALLOWED); + check(access, CREATE_AND_ASSOCIATE_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_EVENTS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FOLDERS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_RECORD_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_ROLES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_TIMEFRAMES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + AccessStatus.DENIED); + check(access, CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_AUDIT_AS_RECORD, + AccessStatus.ALLOWED); + check(access, DECLARE_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_RECORDS_IN_CLOSED_FOLDERS, + AccessStatus.DENIED); + check(access, DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, DELETE_RECORDS, AccessStatus.DENIED); + check(access, DESTROY_RECORDS, AccessStatus.ALLOWED); + check(access, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + AccessStatus.DENIED); + check(access, DISPLAY_RIGHTS_REPORT, + AccessStatus.ALLOWED); + check(access, EDIT_DECLARED_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_NON_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, EDIT_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, ENABLE_DISABLE_AUDIT_BY_TYPES, + AccessStatus.ALLOWED); + check(access, EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, EXTEND_RETENTION_PERIOD_OR_FREEZE, + AccessStatus.ALLOWED); + check(access, MAKE_OPTIONAL_PARAMETERS_MANDATORY, + AccessStatus.ALLOWED); + check(access, MANAGE_ACCESS_CONTROLS, + AccessStatus.ALLOWED); + check(access, MANAGE_ACCESS_RIGHTS, + AccessStatus.ALLOWED); + check(access, MANUALLY_CHANGE_DISPOSITION_DATES, + AccessStatus.ALLOWED); + check(access, MAP_CLASSIFICATION_GUIDE_METADATA, + AccessStatus.ALLOWED); + check(access, MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, PLANNING_REVIEW_CYCLES, + AccessStatus.DENIED); + check(access, RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, SELECT_AUDIT_METADATA, + AccessStatus.ALLOWED); + check(access, TRIGGER_AN_EVENT, AccessStatus.ALLOWED); + check(access, UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, UNFREEZE, AccessStatus.DENIED); + check(access, UPDATE_CLASSIFICATION_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_EXEMPTION_CATEGORIES, + AccessStatus.ALLOWED); + check(access, UPDATE_TRIGGER_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + AccessStatus.ALLOWED); + check(access, UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + AccessStatus.ALLOWED); + check(access, VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, VIEW_UPDATE_REASONS_FOR_FREEZE, + AccessStatus.DENIED); + + return null; + } + }, false, true); + + } + + /** + * Test record folder as administrator + */ + public void testRecordFolderAsAdministrator() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil + .setFullyAuthenticatedUser(rmAdminName); + Map access = securityService + .getCapabilities(rmFolder); + assertEquals(66, access.size()); + check(access, ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, ADD_MODIFY_EVENT_DATES, + AccessStatus.ALLOWED); + check(access, APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + AccessStatus.DENIED); + check(access, ATTACH_RULES_TO_METADATA_PROPERTIES, + AccessStatus.ALLOWED); + check(access, AUTHORIZE_ALL_TRANSFERS, + AccessStatus.DENIED); + check(access, AUTHORIZE_NOMINATED_TRANSFERS, + AccessStatus.DENIED); + check(access, CHANGE_OR_DELETE_REFERENCES, + AccessStatus.UNDETERMINED); + check(access, CLOSE_FOLDERS, AccessStatus.ALLOWED); + check(access, CREATE_AND_ASSOCIATE_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_EVENTS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FOLDERS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_RECORD_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_ROLES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_TIMEFRAMES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + AccessStatus.DENIED); + check(access, CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_AUDIT_AS_RECORD, + AccessStatus.ALLOWED); + check(access, DECLARE_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_RECORDS_IN_CLOSED_FOLDERS, + AccessStatus.DENIED); + check(access, DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, DELETE_RECORDS, AccessStatus.DENIED); + check(access, DESTROY_RECORDS, AccessStatus.ALLOWED); + check(access, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + AccessStatus.DENIED); + check(access, DISPLAY_RIGHTS_REPORT, + AccessStatus.ALLOWED); + check(access, EDIT_DECLARED_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_NON_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, EDIT_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, ENABLE_DISABLE_AUDIT_BY_TYPES, + AccessStatus.ALLOWED); + check(access, EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, EXTEND_RETENTION_PERIOD_OR_FREEZE, + AccessStatus.ALLOWED); + check(access, MAKE_OPTIONAL_PARAMETERS_MANDATORY, + AccessStatus.ALLOWED); + check(access, MANAGE_ACCESS_CONTROLS, + AccessStatus.ALLOWED); + check(access, MANAGE_ACCESS_RIGHTS, + AccessStatus.ALLOWED); + check(access, MANUALLY_CHANGE_DISPOSITION_DATES, + AccessStatus.ALLOWED); + check(access, MAP_CLASSIFICATION_GUIDE_METADATA, + AccessStatus.ALLOWED); + check(access, MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, PLANNING_REVIEW_CYCLES, + AccessStatus.DENIED); + check(access, RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, SELECT_AUDIT_METADATA, + AccessStatus.ALLOWED); + check(access, TRIGGER_AN_EVENT, AccessStatus.ALLOWED); + check(access, UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, UNFREEZE, AccessStatus.DENIED); + check(access, UPDATE_CLASSIFICATION_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_EXEMPTION_CATEGORIES, + AccessStatus.ALLOWED); + check(access, UPDATE_TRIGGER_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + AccessStatus.ALLOWED); + check(access, UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + AccessStatus.ALLOWED); + check(access, VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, VIEW_UPDATE_REASONS_FOR_FREEZE, + AccessStatus.DENIED); + + return null; + } + }, false, true); + } + + /** + * Test record folder as records manager + */ + public void testRecordFolderAsRecordsManager() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil.setFullyAuthenticatedUser(recordsManagerName); + //setFilingOnRecordFolder(rmFolder, recordsManagerName); + Map access = securityService.getCapabilities(rmFolder); + assertEquals(66, access.size()); // 58 + File + check(access, ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, ADD_MODIFY_EVENT_DATES, + AccessStatus.ALLOWED); + check(access, APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + AccessStatus.DENIED); + check(access, ATTACH_RULES_TO_METADATA_PROPERTIES, + AccessStatus.ALLOWED); + check(access, AUTHORIZE_ALL_TRANSFERS, + AccessStatus.DENIED); + check(access, AUTHORIZE_NOMINATED_TRANSFERS, + AccessStatus.DENIED); + check(access, CHANGE_OR_DELETE_REFERENCES, + AccessStatus.UNDETERMINED); + check(access, CLOSE_FOLDERS, AccessStatus.ALLOWED); + check(access, CREATE_AND_ASSOCIATE_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_EVENTS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FOLDERS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_RECORD_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_ROLES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_TIMEFRAMES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + AccessStatus.DENIED); + check(access, CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_AUDIT_AS_RECORD, + AccessStatus.ALLOWED); + check(access, DECLARE_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_RECORDS_IN_CLOSED_FOLDERS, + AccessStatus.DENIED); + check(access, DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, DELETE_RECORDS, AccessStatus.DENIED); + check(access, DESTROY_RECORDS, AccessStatus.ALLOWED); + check(access, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + AccessStatus.DENIED); + check(access, DISPLAY_RIGHTS_REPORT, + AccessStatus.ALLOWED); + check(access, EDIT_DECLARED_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_NON_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, EDIT_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, ENABLE_DISABLE_AUDIT_BY_TYPES, + AccessStatus.ALLOWED); + check(access, EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, EXTEND_RETENTION_PERIOD_OR_FREEZE, + AccessStatus.ALLOWED); + check(access, MAKE_OPTIONAL_PARAMETERS_MANDATORY, + AccessStatus.ALLOWED); + check(access, MANAGE_ACCESS_CONTROLS, + AccessStatus.DENIED); + check(access, MANAGE_ACCESS_RIGHTS, + AccessStatus.ALLOWED); + check(access, MANUALLY_CHANGE_DISPOSITION_DATES, + AccessStatus.ALLOWED); + check(access, MAP_CLASSIFICATION_GUIDE_METADATA, + AccessStatus.ALLOWED); + check(access, MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, PLANNING_REVIEW_CYCLES, + AccessStatus.DENIED); + check(access, RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, SELECT_AUDIT_METADATA, + AccessStatus.ALLOWED); + check(access, TRIGGER_AN_EVENT, AccessStatus.ALLOWED); + check(access, UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, UNFREEZE, AccessStatus.DENIED); + check(access, UPDATE_CLASSIFICATION_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_EXEMPTION_CATEGORIES, + AccessStatus.ALLOWED); + check(access, UPDATE_TRIGGER_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + AccessStatus.ALLOWED); + check(access, UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + AccessStatus.ALLOWED); + check(access, VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, VIEW_UPDATE_REASONS_FOR_FREEZE, + AccessStatus.DENIED); + + return null; + } + }, false, true); + } + + /** + * Test record folder as security officer + */ + public void testRecordFolderAsSecurityOfficer() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil.setFullyAuthenticatedUser(securityOfficerName); + //setFilingOnRecordFolder(rmFolder, securityOfficerName); + Map access = securityService.getCapabilities(rmFolder); + assertEquals(66, access.size()); // 58 + File + check(access, ACCESS_AUDIT, AccessStatus.DENIED); + check(access, ADD_MODIFY_EVENT_DATES, + AccessStatus.ALLOWED); + check(access, APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + AccessStatus.DENIED); + check(access, ATTACH_RULES_TO_METADATA_PROPERTIES, + AccessStatus.DENIED); + check(access, AUTHORIZE_ALL_TRANSFERS, + AccessStatus.DENIED); + check(access, AUTHORIZE_NOMINATED_TRANSFERS, + AccessStatus.DENIED); + check(access, CHANGE_OR_DELETE_REFERENCES, + AccessStatus.DENIED); + check(access, CLOSE_FOLDERS, AccessStatus.ALLOWED); + check(access, CREATE_AND_ASSOCIATE_SELECTION_LISTS, + AccessStatus.DENIED); + check(access, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_EVENTS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FOLDERS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_RECORD_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_ROLES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_TIMEFRAMES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + AccessStatus.DENIED); + check(access, CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_AUDIT_AS_RECORD, + AccessStatus.DENIED); + check(access, DECLARE_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_RECORDS_IN_CLOSED_FOLDERS, + AccessStatus.DENIED); + check(access, DELETE_AUDIT, AccessStatus.DENIED); + check(access, DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, DELETE_RECORDS, AccessStatus.DENIED); + check(access, DESTROY_RECORDS, AccessStatus.DENIED); + check(access, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + AccessStatus.DENIED); + check(access, DISPLAY_RIGHTS_REPORT, + AccessStatus.DENIED); + check(access, EDIT_DECLARED_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_NON_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, EDIT_SELECTION_LISTS, AccessStatus.DENIED); + check(access, ENABLE_DISABLE_AUDIT_BY_TYPES, + AccessStatus.DENIED); + check(access, EXPORT_AUDIT, AccessStatus.DENIED); + check(access, EXTEND_RETENTION_PERIOD_OR_FREEZE, + AccessStatus.DENIED); + check(access, MAKE_OPTIONAL_PARAMETERS_MANDATORY, + AccessStatus.DENIED); + check(access, MANAGE_ACCESS_CONTROLS, + AccessStatus.DENIED); + check(access, MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + check(access, MANUALLY_CHANGE_DISPOSITION_DATES, + AccessStatus.DENIED); + check(access, MAP_CLASSIFICATION_GUIDE_METADATA, + AccessStatus.DENIED); + check(access, MAP_EMAIL_METADATA, AccessStatus.DENIED); + check(access, MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, PASSWORD_CONTROL, AccessStatus.DENIED); + check(access, PLANNING_REVIEW_CYCLES, + AccessStatus.DENIED); + check(access, RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, SELECT_AUDIT_METADATA, + AccessStatus.DENIED); + check(access, TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, UNFREEZE, AccessStatus.DENIED); + check(access, UPDATE_CLASSIFICATION_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_EXEMPTION_CATEGORIES, + AccessStatus.ALLOWED); + check(access, UPDATE_TRIGGER_DATES, AccessStatus.DENIED); + check(access, UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + AccessStatus.DENIED); + check(access, UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + AccessStatus.ALLOWED); + check(access, VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, VIEW_UPDATE_REASONS_FOR_FREEZE, + AccessStatus.DENIED); + + return null; + } + }, false, true); + } + + /** + * Test record folder as power user + */ + public void testRecordFolderAsPowerUser() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil.setFullyAuthenticatedUser(powerUserName); + //setFilingOnRecordFolder(rmFolder, powerUserName); + Map access = securityService.getCapabilities(rmFolder); + assertEquals(66, access.size()); // 58 + File + check(access, ACCESS_AUDIT, AccessStatus.DENIED); + check(access, ADD_MODIFY_EVENT_DATES, + AccessStatus.ALLOWED); + check(access, APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + AccessStatus.DENIED); + check(access, ATTACH_RULES_TO_METADATA_PROPERTIES, + AccessStatus.DENIED); + check(access, AUTHORIZE_ALL_TRANSFERS, + AccessStatus.DENIED); + check(access, AUTHORIZE_NOMINATED_TRANSFERS, + AccessStatus.DENIED); + check(access, CHANGE_OR_DELETE_REFERENCES, + AccessStatus.DENIED); + check(access, CLOSE_FOLDERS, AccessStatus.ALLOWED); + check(access, CREATE_AND_ASSOCIATE_SELECTION_LISTS, + AccessStatus.DENIED); + check(access, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_EVENTS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FOLDERS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_RECORD_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_ROLES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_TIMEFRAMES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + AccessStatus.DENIED); + check(access, CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_AUDIT_AS_RECORD, + AccessStatus.DENIED); + check(access, DECLARE_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_RECORDS_IN_CLOSED_FOLDERS, + AccessStatus.DENIED); + check(access, DELETE_AUDIT, AccessStatus.DENIED); + check(access, DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, DELETE_RECORDS, AccessStatus.DENIED); + check(access, DESTROY_RECORDS, AccessStatus.DENIED); + check(access, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + AccessStatus.DENIED); + check(access, DISPLAY_RIGHTS_REPORT, + AccessStatus.DENIED); + check(access, EDIT_DECLARED_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_NON_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, EDIT_SELECTION_LISTS, AccessStatus.DENIED); + check(access, ENABLE_DISABLE_AUDIT_BY_TYPES, + AccessStatus.DENIED); + check(access, EXPORT_AUDIT, AccessStatus.DENIED); + check(access, EXTEND_RETENTION_PERIOD_OR_FREEZE, + AccessStatus.DENIED); + check(access, MAKE_OPTIONAL_PARAMETERS_MANDATORY, + AccessStatus.DENIED); + check(access, MANAGE_ACCESS_CONTROLS, + AccessStatus.DENIED); + check(access, MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + check(access, MANUALLY_CHANGE_DISPOSITION_DATES, + AccessStatus.DENIED); + check(access, MAP_CLASSIFICATION_GUIDE_METADATA, + AccessStatus.DENIED); + check(access, MAP_EMAIL_METADATA, AccessStatus.DENIED); + check(access, MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, PASSWORD_CONTROL, AccessStatus.DENIED); + check(access, PLANNING_REVIEW_CYCLES, + AccessStatus.DENIED); + check(access, RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, SELECT_AUDIT_METADATA, + AccessStatus.DENIED); + check(access, TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, UNFREEZE, AccessStatus.DENIED); + check(access, UPDATE_CLASSIFICATION_DATES, + AccessStatus.DENIED); + check(access, UPDATE_EXEMPTION_CATEGORIES, + AccessStatus.DENIED); + check(access, UPDATE_TRIGGER_DATES, AccessStatus.DENIED); + check(access, UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + AccessStatus.DENIED); + check(access, UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + AccessStatus.DENIED); + check(access, VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, VIEW_UPDATE_REASONS_FOR_FREEZE, + AccessStatus.DENIED); + + return null; + } + }, false, true); + } + + /** + * Test record folder as user + */ + public void testRecordFolderAsUser() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil + .setFullyAuthenticatedUser(rmUserName); + //setFilingOnRecordFolder(rmFolder, rmUserName); + Map access = securityService + .getCapabilities(rmFolder); + assertEquals(66, access.size()); // 58 + File + check(access, ACCESS_AUDIT, AccessStatus.DENIED); + check(access, ADD_MODIFY_EVENT_DATES, + AccessStatus.DENIED); + check(access, APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + AccessStatus.DENIED); + check(access, ATTACH_RULES_TO_METADATA_PROPERTIES, + AccessStatus.DENIED); + check(access, AUTHORIZE_ALL_TRANSFERS, + AccessStatus.DENIED); + check(access, AUTHORIZE_NOMINATED_TRANSFERS, + AccessStatus.DENIED); + check(access, CHANGE_OR_DELETE_REFERENCES, + AccessStatus.DENIED); + check(access, CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, CREATE_AND_ASSOCIATE_SELECTION_LISTS, + AccessStatus.DENIED); + check(access, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_EVENTS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FOLDERS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_RECORD_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_ROLES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_TIMEFRAMES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + AccessStatus.DENIED); + check(access, CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_AUDIT_AS_RECORD, + AccessStatus.DENIED); + check(access, DECLARE_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_RECORDS_IN_CLOSED_FOLDERS, + AccessStatus.DENIED); + check(access, DELETE_AUDIT, AccessStatus.DENIED); + check(access, DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, DELETE_RECORDS, AccessStatus.DENIED); + check(access, DESTROY_RECORDS, AccessStatus.DENIED); + check(access, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + AccessStatus.DENIED); + check(access, DISPLAY_RIGHTS_REPORT, + AccessStatus.DENIED); + check(access, EDIT_DECLARED_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_NON_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, EDIT_SELECTION_LISTS, AccessStatus.DENIED); + check(access, ENABLE_DISABLE_AUDIT_BY_TYPES, + AccessStatus.DENIED); + check(access, EXPORT_AUDIT, AccessStatus.DENIED); + check(access, EXTEND_RETENTION_PERIOD_OR_FREEZE, + AccessStatus.DENIED); + check(access, MAKE_OPTIONAL_PARAMETERS_MANDATORY, + AccessStatus.DENIED); + check(access, MANAGE_ACCESS_CONTROLS, + AccessStatus.DENIED); + check(access, MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + check(access, MANUALLY_CHANGE_DISPOSITION_DATES, + AccessStatus.DENIED); + check(access, MAP_CLASSIFICATION_GUIDE_METADATA, + AccessStatus.DENIED); + check(access, MAP_EMAIL_METADATA, AccessStatus.DENIED); + check(access, MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, PASSWORD_CONTROL, AccessStatus.DENIED); + check(access, PLANNING_REVIEW_CYCLES, + AccessStatus.DENIED); + check(access, RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, SELECT_AUDIT_METADATA, + AccessStatus.DENIED); + check(access, TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, UNFREEZE, AccessStatus.DENIED); + check(access, UPDATE_CLASSIFICATION_DATES, + AccessStatus.DENIED); + check(access, UPDATE_EXEMPTION_CATEGORIES, + AccessStatus.DENIED); + check(access, UPDATE_TRIGGER_DATES, AccessStatus.DENIED); + check(access, UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + AccessStatus.DENIED); + check(access, UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + AccessStatus.DENIED); + check(access, VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, VIEW_UPDATE_REASONS_FOR_FREEZE, + AccessStatus.DENIED); + + return null; + } + }, false, true); + } + + /** + * Test record as system + */ + public void testRecordAsSystem() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.SYSTEM_USER_NAME); + Map access = securityService.getCapabilities(record); + assertEquals(66, access.size()); + check(access, ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, ADD_MODIFY_EVENT_DATES, + AccessStatus.DENIED); + check(access, APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + AccessStatus.DENIED); + check(access, ATTACH_RULES_TO_METADATA_PROPERTIES, + AccessStatus.ALLOWED); + check(access, AUTHORIZE_ALL_TRANSFERS, + AccessStatus.DENIED); + check(access, AUTHORIZE_NOMINATED_TRANSFERS, + AccessStatus.DENIED); + check(access, CHANGE_OR_DELETE_REFERENCES, + AccessStatus.UNDETERMINED); + check(access, CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, CREATE_AND_ASSOCIATE_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_EVENTS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FOLDERS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_RECORD_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_ROLES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_TIMEFRAMES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + AccessStatus.DENIED); + check(access, CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_AUDIT_AS_RECORD, + AccessStatus.ALLOWED); + check(access, DECLARE_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_RECORDS_IN_CLOSED_FOLDERS, + AccessStatus.DENIED); + check(access, DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, DELETE_RECORDS, AccessStatus.ALLOWED); + check(access, DESTROY_RECORDS, AccessStatus.DENIED); + check(access, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + AccessStatus.DENIED); + check(access, DISPLAY_RIGHTS_REPORT, + AccessStatus.ALLOWED); + check(access, EDIT_DECLARED_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_NON_RECORD_METADATA, + AccessStatus.ALLOWED); + check(access, EDIT_RECORD_METADATA, + AccessStatus.ALLOWED); + check(access, EDIT_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, ENABLE_DISABLE_AUDIT_BY_TYPES, + AccessStatus.ALLOWED); + check(access, EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, EXTEND_RETENTION_PERIOD_OR_FREEZE, + AccessStatus.ALLOWED); + check(access, MAKE_OPTIONAL_PARAMETERS_MANDATORY, + AccessStatus.ALLOWED); + check(access, MANAGE_ACCESS_CONTROLS, + AccessStatus.ALLOWED); + check(access, MANAGE_ACCESS_RIGHTS, + AccessStatus.ALLOWED); + check(access, MANUALLY_CHANGE_DISPOSITION_DATES, + AccessStatus.DENIED); + check(access, MAP_CLASSIFICATION_GUIDE_METADATA, + AccessStatus.ALLOWED); + check(access, MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, PLANNING_REVIEW_CYCLES, + AccessStatus.DENIED); + check(access, RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, SELECT_AUDIT_METADATA, + AccessStatus.ALLOWED); + check(access, TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, UNFREEZE, AccessStatus.DENIED); + check(access, UPDATE_CLASSIFICATION_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_EXEMPTION_CATEGORIES, + AccessStatus.ALLOWED); + check(access, UPDATE_TRIGGER_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + AccessStatus.ALLOWED); + check(access, UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + AccessStatus.ALLOWED); + check(access, VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, VIEW_UPDATE_REASONS_FOR_FREEZE, + AccessStatus.DENIED); + + return null; + } + }, false, true); + } + + /** + * Test record as admin + */ + public void testRecordAsAdmin() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil + .setFullyAuthenticatedUser(AuthenticationUtil + .getAdminUserName()); + Map access = securityService + .getCapabilities(record); + assertEquals(66, access.size()); + check(access, ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, ADD_MODIFY_EVENT_DATES, + AccessStatus.DENIED); + check(access, APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + AccessStatus.DENIED); + check(access, ATTACH_RULES_TO_METADATA_PROPERTIES, + AccessStatus.ALLOWED); + check(access, AUTHORIZE_ALL_TRANSFERS, + AccessStatus.DENIED); + check(access, AUTHORIZE_NOMINATED_TRANSFERS, + AccessStatus.DENIED); + check(access, CHANGE_OR_DELETE_REFERENCES, + AccessStatus.UNDETERMINED); + check(access, CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, CREATE_AND_ASSOCIATE_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_EVENTS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FOLDERS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_RECORD_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_ROLES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_TIMEFRAMES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + AccessStatus.DENIED); + check(access, CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_AUDIT_AS_RECORD, + AccessStatus.ALLOWED); + check(access, DECLARE_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_RECORDS_IN_CLOSED_FOLDERS, + AccessStatus.DENIED); + check(access, DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, DELETE_RECORDS, AccessStatus.ALLOWED); + check(access, DESTROY_RECORDS, AccessStatus.DENIED); + check(access, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + AccessStatus.DENIED); + check(access, DISPLAY_RIGHTS_REPORT, + AccessStatus.ALLOWED); + check(access, EDIT_DECLARED_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_NON_RECORD_METADATA, + AccessStatus.ALLOWED); + check(access, EDIT_RECORD_METADATA, + AccessStatus.ALLOWED); + check(access, EDIT_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, ENABLE_DISABLE_AUDIT_BY_TYPES, + AccessStatus.ALLOWED); + check(access, EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, EXTEND_RETENTION_PERIOD_OR_FREEZE, + AccessStatus.ALLOWED); + check(access, MAKE_OPTIONAL_PARAMETERS_MANDATORY, + AccessStatus.ALLOWED); + check(access, MANAGE_ACCESS_CONTROLS, + AccessStatus.ALLOWED); + check(access, MANAGE_ACCESS_RIGHTS, + AccessStatus.ALLOWED); + check(access, MANUALLY_CHANGE_DISPOSITION_DATES, + AccessStatus.DENIED); + check(access, MAP_CLASSIFICATION_GUIDE_METADATA, + AccessStatus.ALLOWED); + check(access, MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, PLANNING_REVIEW_CYCLES, + AccessStatus.DENIED); + check(access, RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, SELECT_AUDIT_METADATA, + AccessStatus.ALLOWED); + check(access, TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, UNFREEZE, AccessStatus.DENIED); + check(access, UPDATE_CLASSIFICATION_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_EXEMPTION_CATEGORIES, + AccessStatus.ALLOWED); + check(access, UPDATE_TRIGGER_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + AccessStatus.ALLOWED); + check(access, UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + AccessStatus.ALLOWED); + check(access, VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, VIEW_UPDATE_REASONS_FOR_FREEZE, + AccessStatus.DENIED); + + return null; + } + }, false, true); + } + + /** + * Test record as administrator + */ + public void testRecordAsAdministrator() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil + .setFullyAuthenticatedUser(rmAdminName); + Map access = securityService + .getCapabilities(record); + assertEquals(66, access.size()); + check(access, ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, ADD_MODIFY_EVENT_DATES, + AccessStatus.DENIED); + check(access, APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + AccessStatus.DENIED); + check(access, ATTACH_RULES_TO_METADATA_PROPERTIES, + AccessStatus.ALLOWED); + check(access, AUTHORIZE_ALL_TRANSFERS, + AccessStatus.DENIED); + check(access, AUTHORIZE_NOMINATED_TRANSFERS, + AccessStatus.DENIED); + check(access, CHANGE_OR_DELETE_REFERENCES, + AccessStatus.UNDETERMINED); + check(access, CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, CREATE_AND_ASSOCIATE_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_EVENTS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FOLDERS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_RECORD_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_ROLES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_TIMEFRAMES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + AccessStatus.DENIED); + check(access, CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_AUDIT_AS_RECORD, + AccessStatus.ALLOWED); + check(access, DECLARE_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_RECORDS_IN_CLOSED_FOLDERS, + AccessStatus.DENIED); + check(access, DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, DELETE_RECORDS, AccessStatus.ALLOWED); + check(access, DESTROY_RECORDS, AccessStatus.DENIED); + check(access, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + AccessStatus.DENIED); + check(access, DISPLAY_RIGHTS_REPORT, + AccessStatus.ALLOWED); + check(access, EDIT_DECLARED_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_NON_RECORD_METADATA, + AccessStatus.ALLOWED); + check(access, EDIT_RECORD_METADATA, + AccessStatus.ALLOWED); + check(access, EDIT_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, ENABLE_DISABLE_AUDIT_BY_TYPES, + AccessStatus.ALLOWED); + check(access, EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, EXTEND_RETENTION_PERIOD_OR_FREEZE, + AccessStatus.ALLOWED); + check(access, MAKE_OPTIONAL_PARAMETERS_MANDATORY, + AccessStatus.ALLOWED); + check(access, MANAGE_ACCESS_CONTROLS, + AccessStatus.ALLOWED); + check(access, MANAGE_ACCESS_RIGHTS, + AccessStatus.ALLOWED); + check(access, MANUALLY_CHANGE_DISPOSITION_DATES, + AccessStatus.DENIED); + check(access, MAP_CLASSIFICATION_GUIDE_METADATA, + AccessStatus.ALLOWED); + check(access, MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, PLANNING_REVIEW_CYCLES, + AccessStatus.DENIED); + check(access, RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, SELECT_AUDIT_METADATA, + AccessStatus.ALLOWED); + check(access, TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, UNFREEZE, AccessStatus.DENIED); + check(access, UPDATE_CLASSIFICATION_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_EXEMPTION_CATEGORIES, + AccessStatus.ALLOWED); + check(access, UPDATE_TRIGGER_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + AccessStatus.ALLOWED); + check(access, UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + AccessStatus.ALLOWED); + check(access, VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, VIEW_UPDATE_REASONS_FOR_FREEZE, + AccessStatus.DENIED); + + return null; + } + }, false, true); + } + + /** + * Test record as records manager + */ + public void testRecordAsRecordsManager() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil + .setFullyAuthenticatedUser(recordsManagerName); + // setFilingOnRecord(record, recordsManagerName); + Map access = securityService + .getCapabilities(record); + assertEquals(66, access.size()); // 58 + File + check(access, ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, ADD_MODIFY_EVENT_DATES, + AccessStatus.DENIED); + check(access, APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + AccessStatus.DENIED); + check(access, ATTACH_RULES_TO_METADATA_PROPERTIES, + AccessStatus.ALLOWED); + check(access, AUTHORIZE_ALL_TRANSFERS, + AccessStatus.DENIED); + check(access, AUTHORIZE_NOMINATED_TRANSFERS, + AccessStatus.DENIED); + check(access, CHANGE_OR_DELETE_REFERENCES, + AccessStatus.UNDETERMINED); + check(access, CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, CREATE_AND_ASSOCIATE_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_EVENTS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_FOLDERS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_RECORD_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_ROLES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_TIMEFRAMES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + AccessStatus.DENIED); + check(access, CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_AUDIT_AS_RECORD, + AccessStatus.ALLOWED); + check(access, DECLARE_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_RECORDS_IN_CLOSED_FOLDERS, + AccessStatus.DENIED); + check(access, DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, DELETE_RECORDS, AccessStatus.ALLOWED); + check(access, DESTROY_RECORDS, AccessStatus.DENIED); + check(access, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + AccessStatus.DENIED); + check(access, DISPLAY_RIGHTS_REPORT, + AccessStatus.ALLOWED); + check(access, EDIT_DECLARED_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_NON_RECORD_METADATA, + AccessStatus.ALLOWED); + check(access, EDIT_RECORD_METADATA, + AccessStatus.ALLOWED); + check(access, EDIT_SELECTION_LISTS, + AccessStatus.ALLOWED); + check(access, ENABLE_DISABLE_AUDIT_BY_TYPES, + AccessStatus.ALLOWED); + check(access, EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, EXTEND_RETENTION_PERIOD_OR_FREEZE, + AccessStatus.ALLOWED); + check(access, MAKE_OPTIONAL_PARAMETERS_MANDATORY, + AccessStatus.ALLOWED); + check(access, MANAGE_ACCESS_CONTROLS, + AccessStatus.DENIED); + check(access, MANAGE_ACCESS_RIGHTS, + AccessStatus.ALLOWED); + check(access, MANUALLY_CHANGE_DISPOSITION_DATES, + AccessStatus.DENIED); + check(access, MAP_CLASSIFICATION_GUIDE_METADATA, + AccessStatus.ALLOWED); + check(access, MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, PLANNING_REVIEW_CYCLES, + AccessStatus.DENIED); + check(access, RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, SELECT_AUDIT_METADATA, + AccessStatus.ALLOWED); + check(access, TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, UNFREEZE, AccessStatus.DENIED); + check(access, UPDATE_CLASSIFICATION_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_EXEMPTION_CATEGORIES, + AccessStatus.ALLOWED); + check(access, UPDATE_TRIGGER_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + AccessStatus.ALLOWED); + check(access, UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + AccessStatus.ALLOWED); + check(access, VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, VIEW_UPDATE_REASONS_FOR_FREEZE, + AccessStatus.DENIED); + + return null; + } + }, false, true); + } + + /** + * Test record as security officer + */ + public void testRecordAsSecurityOfficer() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil + .setFullyAuthenticatedUser(securityOfficerName); + // setFilingOnRecord(record, securityOfficerName); + Map access = securityService + .getCapabilities(record); + assertEquals(66, access.size()); // 58 + File + check(access, ACCESS_AUDIT, AccessStatus.DENIED); + check(access, ADD_MODIFY_EVENT_DATES, + AccessStatus.DENIED); + check(access, APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + AccessStatus.DENIED); + check(access, ATTACH_RULES_TO_METADATA_PROPERTIES, + AccessStatus.DENIED); + check(access, AUTHORIZE_ALL_TRANSFERS, + AccessStatus.DENIED); + check(access, AUTHORIZE_NOMINATED_TRANSFERS, + AccessStatus.DENIED); + check(access, CHANGE_OR_DELETE_REFERENCES, + AccessStatus.DENIED); + check(access, CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, CREATE_AND_ASSOCIATE_SELECTION_LISTS, + AccessStatus.DENIED); + check(access, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + AccessStatus.ALLOWED); + check(access, CREATE_MODIFY_DESTROY_EVENTS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FOLDERS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_RECORD_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_ROLES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_TIMEFRAMES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + AccessStatus.DENIED); + check(access, CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_AUDIT_AS_RECORD, + AccessStatus.DENIED); + check(access, DECLARE_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_RECORDS_IN_CLOSED_FOLDERS, + AccessStatus.DENIED); + check(access, DELETE_AUDIT, AccessStatus.DENIED); + check(access, DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, DELETE_RECORDS, AccessStatus.DENIED); + check(access, DESTROY_RECORDS, AccessStatus.DENIED); + check(access, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + AccessStatus.DENIED); + check(access, DISPLAY_RIGHTS_REPORT, + AccessStatus.DENIED); + check(access, EDIT_DECLARED_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_NON_RECORD_METADATA, + AccessStatus.ALLOWED); + check(access, EDIT_RECORD_METADATA, + AccessStatus.ALLOWED); + check(access, EDIT_SELECTION_LISTS, AccessStatus.DENIED); + check(access, ENABLE_DISABLE_AUDIT_BY_TYPES, + AccessStatus.DENIED); + check(access, EXPORT_AUDIT, AccessStatus.DENIED); + check(access, EXTEND_RETENTION_PERIOD_OR_FREEZE, + AccessStatus.DENIED); + check(access, MAKE_OPTIONAL_PARAMETERS_MANDATORY, + AccessStatus.DENIED); + check(access, MANAGE_ACCESS_CONTROLS, + AccessStatus.DENIED); + check(access, MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + check(access, MANUALLY_CHANGE_DISPOSITION_DATES, + AccessStatus.DENIED); + check(access, MAP_CLASSIFICATION_GUIDE_METADATA, + AccessStatus.DENIED); + check(access, MAP_EMAIL_METADATA, AccessStatus.DENIED); + check(access, MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, PASSWORD_CONTROL, AccessStatus.DENIED); + check(access, PLANNING_REVIEW_CYCLES, + AccessStatus.DENIED); + check(access, RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, SELECT_AUDIT_METADATA, + AccessStatus.DENIED); + check(access, TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, UNFREEZE, AccessStatus.DENIED); + check(access, UPDATE_CLASSIFICATION_DATES, + AccessStatus.ALLOWED); + check(access, UPDATE_EXEMPTION_CATEGORIES, + AccessStatus.ALLOWED); + check(access, UPDATE_TRIGGER_DATES, AccessStatus.DENIED); + check(access, UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + AccessStatus.DENIED); + check(access, UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + AccessStatus.ALLOWED); + check(access, VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, VIEW_UPDATE_REASONS_FOR_FREEZE, + AccessStatus.DENIED); + + return null; + } + }, false, true); + } + + /** + * Test records as power user + */ + public void testRecordAsPowerUser() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + + AuthenticationUtil + .setFullyAuthenticatedUser(powerUserName); + // setFilingOnRecord(record, powerUserName); + Map access = securityService + .getCapabilities(record); + assertEquals(66, access.size()); // 58 + File + check(access, ACCESS_AUDIT, AccessStatus.DENIED); + check(access, ADD_MODIFY_EVENT_DATES, + AccessStatus.DENIED); + check(access, APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + AccessStatus.DENIED); + check(access, ATTACH_RULES_TO_METADATA_PROPERTIES, + AccessStatus.DENIED); + check(access, AUTHORIZE_ALL_TRANSFERS, + AccessStatus.DENIED); + check(access, AUTHORIZE_NOMINATED_TRANSFERS, + AccessStatus.DENIED); + check(access, CHANGE_OR_DELETE_REFERENCES, + AccessStatus.DENIED); + check(access, CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, CREATE_AND_ASSOCIATE_SELECTION_LISTS, + AccessStatus.DENIED); + check(access, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_EVENTS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FOLDERS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_RECORD_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_ROLES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_TIMEFRAMES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + AccessStatus.DENIED); + check(access, CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_AUDIT_AS_RECORD, + AccessStatus.DENIED); + check(access, DECLARE_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_RECORDS_IN_CLOSED_FOLDERS, + AccessStatus.DENIED); + check(access, DELETE_AUDIT, AccessStatus.DENIED); + check(access, DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, DELETE_RECORDS, AccessStatus.DENIED); + check(access, DESTROY_RECORDS, AccessStatus.DENIED); + check(access, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + AccessStatus.DENIED); + check(access, DISPLAY_RIGHTS_REPORT, + AccessStatus.DENIED); + check(access, EDIT_DECLARED_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_NON_RECORD_METADATA, + AccessStatus.ALLOWED); + check(access, EDIT_RECORD_METADATA, + AccessStatus.ALLOWED); + check(access, EDIT_SELECTION_LISTS, AccessStatus.DENIED); + check(access, ENABLE_DISABLE_AUDIT_BY_TYPES, + AccessStatus.DENIED); + check(access, EXPORT_AUDIT, AccessStatus.DENIED); + check(access, EXTEND_RETENTION_PERIOD_OR_FREEZE, + AccessStatus.DENIED); + check(access, MAKE_OPTIONAL_PARAMETERS_MANDATORY, + AccessStatus.DENIED); + check(access, MANAGE_ACCESS_CONTROLS, + AccessStatus.DENIED); + check(access, MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + check(access, MANUALLY_CHANGE_DISPOSITION_DATES, + AccessStatus.DENIED); + check(access, MAP_CLASSIFICATION_GUIDE_METADATA, + AccessStatus.DENIED); + check(access, MAP_EMAIL_METADATA, AccessStatus.DENIED); + check(access, MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, PASSWORD_CONTROL, AccessStatus.DENIED); + check(access, PLANNING_REVIEW_CYCLES, + AccessStatus.DENIED); + check(access, RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, SELECT_AUDIT_METADATA, + AccessStatus.DENIED); + check(access, TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, UNFREEZE, AccessStatus.DENIED); + check(access, UPDATE_CLASSIFICATION_DATES, + AccessStatus.DENIED); + check(access, UPDATE_EXEMPTION_CATEGORIES, + AccessStatus.DENIED); + check(access, UPDATE_TRIGGER_DATES, AccessStatus.DENIED); + check(access, UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + AccessStatus.DENIED); + check(access, UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + AccessStatus.DENIED); + check(access, VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, VIEW_UPDATE_REASONS_FOR_FREEZE, + AccessStatus.DENIED); + + return null; + } + }, false, true); + } + + /** + * Test record as user + */ + public void testRecordAsUser() + { + retryingTransactionHelper.doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil + .setFullyAuthenticatedUser(rmUserName); + // setFilingOnRecord(record, rmUserName); + Map access = securityService + .getCapabilities(record); + assertEquals(66, access.size()); // 58 + File + check(access, ACCESS_AUDIT, AccessStatus.DENIED); + check(access, ADD_MODIFY_EVENT_DATES, + AccessStatus.DENIED); + check(access, APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + AccessStatus.DENIED); + check(access, ATTACH_RULES_TO_METADATA_PROPERTIES, + AccessStatus.DENIED); + check(access, AUTHORIZE_ALL_TRANSFERS, + AccessStatus.DENIED); + check(access, AUTHORIZE_NOMINATED_TRANSFERS, + AccessStatus.DENIED); + check(access, CHANGE_OR_DELETE_REFERENCES, + AccessStatus.DENIED); + check(access, CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, CREATE_AND_ASSOCIATE_SELECTION_LISTS, + AccessStatus.DENIED); + check(access, + CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_EVENTS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_FOLDERS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_RECORD_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_REFERENCE_TYPES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_ROLES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_TIMEFRAMES, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, + AccessStatus.DENIED); + check(access, CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, + AccessStatus.DENIED); + check(access, CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_AUDIT_AS_RECORD, + AccessStatus.DENIED); + check(access, DECLARE_RECORDS, AccessStatus.DENIED); + check(access, DECLARE_RECORDS_IN_CLOSED_FOLDERS, + AccessStatus.DENIED); + check(access, DELETE_AUDIT, AccessStatus.DENIED); + check(access, DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, DELETE_RECORDS, AccessStatus.DENIED); + check(access, DESTROY_RECORDS, AccessStatus.DENIED); + check(access, + DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, + AccessStatus.DENIED); + check(access, DISPLAY_RIGHTS_REPORT, + AccessStatus.DENIED); + check(access, EDIT_DECLARED_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_NON_RECORD_METADATA, + AccessStatus.DENIED); + check(access, EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, EDIT_SELECTION_LISTS, AccessStatus.DENIED); + check(access, ENABLE_DISABLE_AUDIT_BY_TYPES, + AccessStatus.DENIED); + check(access, EXPORT_AUDIT, AccessStatus.DENIED); + check(access, EXTEND_RETENTION_PERIOD_OR_FREEZE, + AccessStatus.DENIED); + check(access, MAKE_OPTIONAL_PARAMETERS_MANDATORY, + AccessStatus.DENIED); + check(access, MANAGE_ACCESS_CONTROLS, + AccessStatus.DENIED); + check(access, MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + check(access, MANUALLY_CHANGE_DISPOSITION_DATES, + AccessStatus.DENIED); + check(access, MAP_CLASSIFICATION_GUIDE_METADATA, + AccessStatus.DENIED); + check(access, MAP_EMAIL_METADATA, AccessStatus.DENIED); + check(access, MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, PASSWORD_CONTROL, AccessStatus.DENIED); + check(access, PLANNING_REVIEW_CYCLES, + AccessStatus.DENIED); + check(access, RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, SELECT_AUDIT_METADATA, + AccessStatus.DENIED); + check(access, TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, UNFREEZE, AccessStatus.DENIED); + check(access, UPDATE_CLASSIFICATION_DATES, + AccessStatus.DENIED); + check(access, UPDATE_EXEMPTION_CATEGORIES, + AccessStatus.DENIED); + check(access, UPDATE_TRIGGER_DATES, AccessStatus.DENIED); + check(access, UPDATE_VITAL_RECORD_CYCLE_INFORMATION, + AccessStatus.DENIED); + check(access, UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, + AccessStatus.DENIED); + check(access, VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, VIEW_UPDATE_REASONS_FOR_FREEZE, + AccessStatus.DENIED); + + return null; + } + }, false, true); + } + + // private void setFilingOnRecord(NodeRef record, String authority) + // { + // NodeRef recordFolder = + // nodeService.getPrimaryParent(record).getParentRef(); + // permissionService.setPermission(recordFolder, authority, FILING, true); + // permissionService.setPermission(nodeService.getPrimaryParent(recordFolder).getParentRef(), + // authority, READ_RECORDS, true); + // } + // + // private void setFilingOnRecordFolder(NodeRef recordFolder, String + // authority) + // { + // permissionService.setPermission(recordFolder, authority, FILING, true); + // permissionService.setPermission(nodeService.getPrimaryParent(recordFolder).getParentRef(), + // authority, READ_RECORDS, true); + // } +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/capabilities/CompositeCapabilityTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/capabilities/CompositeCapabilityTest.java new file mode 100644 index 0000000000..cbdbe5439c --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/capabilities/CompositeCapabilityTest.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.capabilities; + +import org.alfresco.module.org_alfresco_module_rm.capability.Capability; +import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.AccessStatus; + +/** + * Declarative capability unit test + * + * @author Roy Wetherall + */ +public class CompositeCapabilityTest extends BaseRMTestCase +{ + private NodeRef record; + private NodeRef declaredRecord; + + @Override + protected boolean isUserTest() + { + return true; + } + + @Override + protected void setupTestDataImpl() + { + super.setupTestDataImpl(); + + // Pre-filed content + record = utils.createRecord(rmFolder, "record.txt"); + declaredRecord = utils.createRecord(rmFolder, "declaredRecord.txt"); + } + + @Override + protected void setupTestData() + { + super.setupTestData(); + + retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + + utils.declareRecord(declaredRecord); + + return null; + } + }); + } + + @Override + protected void tearDownImpl() + { + super.tearDownImpl(); + } + + @Override + protected void setupTestUsersImpl(NodeRef filePlan) + { + super.setupTestUsersImpl(filePlan); + + // Give all the users file permission objects + for (String user : testUsers) + { + securityService.setPermission(rmContainer, user, RMPermissionModel.FILING); + } + } + + public void testUpdate() + { + final Capability capability = capabilityService.getCapability("Update"); + assertNotNull(capability); + + doTestInTransaction(new Test() + { + @Override + public Void run() + { + assertEquals(AccessStatus.ALLOWED, capability.hasPermission(rmContainer)); + assertEquals(AccessStatus.ALLOWED, capability.hasPermission(rmFolder)); + assertEquals(AccessStatus.ALLOWED, capability.hasPermission(record)); + assertEquals(AccessStatus.ALLOWED, capability.hasPermission(declaredRecord)); + + return null; + } + }, recordsManagerName); + + doTestInTransaction(new Test() + { + @Override + public Void run() + { + assertEquals(AccessStatus.DENIED, capability.hasPermission(rmContainer)); + assertEquals(AccessStatus.DENIED, capability.hasPermission(rmFolder)); + assertEquals(AccessStatus.DENIED, capability.hasPermission(record)); + assertEquals(AccessStatus.DENIED, capability.hasPermission(declaredRecord)); + + return null; + } + }, userName); + + + } + + public void testUpdateProperties() + { + final Capability capability = capabilityService.getCapability("UpdateProperties"); + assertNotNull(capability); + + doTestInTransaction(new Test() + { + @Override + public Void run() + { + assertEquals(AccessStatus.ALLOWED, capability.hasPermission(rmContainer)); + assertEquals(AccessStatus.ALLOWED, capability.hasPermission(rmFolder)); + assertEquals(AccessStatus.ALLOWED, capability.hasPermission(record)); + assertEquals(AccessStatus.ALLOWED, capability.hasPermission(declaredRecord)); + + return null; + } + }, recordsManagerName); + + doTestInTransaction(new Test() + { + @Override + public Void run() + { + assertEquals(AccessStatus.DENIED, capability.hasPermission(rmContainer)); + assertEquals(AccessStatus.DENIED, capability.hasPermission(rmFolder)); + assertEquals(AccessStatus.DENIED, capability.hasPermission(record)); + assertEquals(AccessStatus.DENIED, capability.hasPermission(declaredRecord)); + + return null; + } + }, userName); + + + } +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/capabilities/DeclarativeCapabilityTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/capabilities/DeclarativeCapabilityTest.java new file mode 100644 index 0000000000..f764510b43 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/capabilities/DeclarativeCapabilityTest.java @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.capabilities; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.FilePlanComponentKind; +import org.alfresco.module.org_alfresco_module_rm.capability.Capability; +import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; +import org.alfresco.module.org_alfresco_module_rm.capability.declarative.CapabilityCondition; +import org.alfresco.module.org_alfresco_module_rm.capability.declarative.DeclarativeCapability; +import org.alfresco.module.org_alfresco_module_rm.security.Role; +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.AccessStatus; + +/** + * Declarative capability unit test + * + * @author Roy Wetherall + */ +public class DeclarativeCapabilityTest extends BaseRMTestCase +{ + private NodeRef record; + private NodeRef declaredRecord; + + private NodeRef recordFolderContainsFrozen; + private NodeRef frozenRecord; + private NodeRef frozenRecord2; + private NodeRef frozenRecordFolder; + + private NodeRef closedFolder; + + @Override + protected boolean isUserTest() + { + return true; + } + + @Override + protected void setupTestDataImpl() + { + super.setupTestDataImpl(); + + // Pre-filed content + record = utils.createRecord(rmFolder, "record.txt"); + declaredRecord = utils.createRecord(rmFolder, "declaredRecord.txt"); + + // Closed folder + closedFolder = rmService.createRecordFolder(rmContainer, "closedFolder"); + utils.closeFolder(closedFolder); + + recordFolderContainsFrozen = rmService.createRecordFolder(rmContainer, "containsFrozen"); + frozenRecord = utils.createRecord(rmFolder, "frozenRecord.txt"); + frozenRecord2 = utils.createRecord(recordFolderContainsFrozen, "frozen2.txt"); + frozenRecordFolder = rmService.createRecordFolder(rmContainer, "frozenRecordFolder"); + + } + + @Override + protected void setupTestData() + { + super.setupTestData(); + + retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + + utils.declareRecord(declaredRecord); + utils.declareRecord(frozenRecord); + utils.declareRecord(frozenRecord2); + utils.freeze(frozenRecord); + utils.freeze(frozenRecordFolder); + utils.freeze(frozenRecord2); + + return null; + } + }); + } + + @Override + protected void tearDownImpl() + { + // Unfreeze stuff so it can be deleted + utils.unfreeze(frozenRecord); + utils.unfreeze(frozenRecordFolder); + utils.unfreeze(frozenRecord2); + + super.tearDownImpl(); + } + + @Override + protected void setupTestUsersImpl(NodeRef filePlan) + { + super.setupTestUsersImpl(filePlan); + + // Give all the users file permission objects + for (String user : testUsers) + { + securityService.setPermission(rmFolder, user, RMPermissionModel.FILING); + } + } + + public void testDeclarativeCapabilities() + { + Set capabilities = capabilityService.getCapabilities(); + for (Capability capability : capabilities) + { + if (capability instanceof DeclarativeCapability && + capability.isPrivate() == false && + capability.getName().equals("MoveRecords") == false && + capability.getName().equals("DeleteLinks") == false && + capability.getName().equals("ChangeOrDeleteReferences") == false && + capability.getActionNames().isEmpty() == true) + { + testDeclarativeCapability((DeclarativeCapability)capability); + } + } + } + + private void testDeclarativeCapability(final DeclarativeCapability capability) + { + for (String user : testUsers) + { + testDeclarativeCapability(capability, user, filePlan); + testDeclarativeCapability(capability, user, rmContainer); + testDeclarativeCapability(capability, user, rmFolder); + testDeclarativeCapability(capability, user, record); + } + } + + private void testDeclarativeCapability(final DeclarativeCapability capability, final String userName, final NodeRef filePlanComponent) + { + doTestInTransaction(new Test() + { + @Override + public Void run() + { + AccessStatus accessStatus = capability.hasPermission(filePlanComponent); + + Set roles = securityService.getRolesByUser(filePlan, userName); + if (roles.isEmpty() == true) + { + assertEquals("User " + userName + " has no RM role so we expect access to be denied for capability " + capability.getName(), + AccessStatus.DENIED, + accessStatus); + } + else + { + // Do the kind check here ... + FilePlanComponentKind actualKind = rmService.getFilePlanComponentKind(filePlanComponent); + List kinds = capability.getKinds(); + + if (kinds == null || + kinds.contains(actualKind.toString()) == true) + { + Map conditions = capability.getConditions(); + boolean conditionResult = getConditionResult(filePlanComponent, conditions); + + assertEquals("User is expected to only have one role.", 1, roles.size()); + Role role = new ArrayList(roles).get(0); + assertNotNull(role); + + Set roleCapabilities = role.getCapabilities(); + if (roleCapabilities.contains(capability.getName()) == true && conditionResult == true) + { + assertEquals("User " + userName + " has the role " + role.getDisplayLabel() + + " so we expect access to be allowed for capability " + capability.getName() + " on the object " + + (String)nodeService.getProperty(filePlanComponent, ContentModel.PROP_NAME), + AccessStatus.ALLOWED, + accessStatus); + } + else + { + assertEquals("User " + userName + " has the role " + role.getDisplayLabel() + " so we expect access to be denied for capability " + capability.getName(), + AccessStatus.DENIED, + accessStatus); + } + } + else + { + // Expect fail since the kind is not expected by the capability + assertEquals("NodeRef is of kind" + actualKind + " so we expect access to be denied for capability " + capability.getName(), + AccessStatus.DENIED, + accessStatus); + } + } + + return null; + } + }, userName); + } + + private boolean getConditionResult(NodeRef nodeRef, Map conditions) + { + boolean result = true; + + if (conditions != null && conditions.size() != 0) + { + for (Map.Entry entry : conditions.entrySet()) + { + // Get the condition bean + CapabilityCondition condition = (CapabilityCondition)applicationContext.getBean(entry.getKey()); + assertNotNull("Invalid condition name.", condition); + + boolean actual = condition.evaluate(nodeRef); + if (actual != entry.getValue().booleanValue()) + { + result = false; + break; + } + } + } + + return result; + } + + /** Specific declarative capability tests */ + + public void testFileCapability() + { + final Capability capability = capabilityService.getCapability("File"); + assertNotNull(capability); + + doTestInTransaction(new Test() + { + @Override + public Void run() + { + assertEquals(AccessStatus.DENIED, capability.hasPermission(rmContainer)); + assertEquals(AccessStatus.ALLOWED, capability.hasPermission(rmFolder)); + assertEquals(AccessStatus.ALLOWED, capability.hasPermission(record)); + assertEquals(AccessStatus.DENIED, capability.hasPermission(declaredRecord)); + assertEquals(AccessStatus.DENIED, capability.hasPermission(frozenRecordFolder)); + assertEquals(AccessStatus.DENIED, capability.hasPermission(recordFolderContainsFrozen)); + assertEquals(AccessStatus.DENIED, capability.hasPermission(frozenRecord)); + assertEquals(AccessStatus.DENIED, capability.hasPermission(closedFolder)); + + return null; + } + }, recordsManagerName); + + doTestInTransaction(new Test() + { + @Override + public Void run() + { + assertEquals(AccessStatus.DENIED, capability.hasPermission(rmContainer)); + assertEquals(AccessStatus.ALLOWED, capability.hasPermission(rmFolder)); + assertEquals(AccessStatus.ALLOWED, capability.hasPermission(record)); + assertEquals(AccessStatus.DENIED, capability.hasPermission(declaredRecord)); + assertEquals(AccessStatus.DENIED, capability.hasPermission(frozenRecordFolder)); + assertEquals(AccessStatus.DENIED, capability.hasPermission(recordFolderContainsFrozen)); + assertEquals(AccessStatus.DENIED, capability.hasPermission(frozenRecord)); + assertEquals(AccessStatus.DENIED, capability.hasPermission(closedFolder)); + + return null; + } + }, rmUserName); + } +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/jscript/CapabilitiesTest.js b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/jscript/CapabilitiesTest.js new file mode 100644 index 0000000000..fe05c9a9d6 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/jscript/CapabilitiesTest.js @@ -0,0 +1,18 @@ +function main() +{ + test.assertNotNull(filePlan); + test.assertNotNull(record); + + var rmNode = rmService.getRecordsManagementNode(record); + test.assertNotNull(rmNode); + + var capabilities = rmNode.capabilities; + var countCheck = capabilities.length != 0; + test.assertTrue(countCheck); + + var capability = capabilities[0]; + test.assertNotNull(capability); + test.assertNotNull(capability.name); +} + +main(); \ No newline at end of file diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/jscript/JSONConversionComponentTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/jscript/JSONConversionComponentTest.java new file mode 100644 index 0000000000..ab7d25b4ca --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/jscript/JSONConversionComponentTest.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.jscript; + +import java.io.Serializable; + +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; +import org.alfresco.repo.jscript.app.JSONConversionComponent; +import org.alfresco.service.cmr.repository.NodeRef; +import org.apache.commons.lang.ArrayUtils; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * @author Roy Wetherall + */ +public class JSONConversionComponentTest extends BaseRMTestCase +{ + private JSONConversionComponent converter; + + private NodeRef record; + + @Override + protected void initServices() + { + super.initServices(); + converter = (JSONConversionComponent)applicationContext.getBean("jsonConversionComponent"); + } + + @Override + protected void setupTestDataImpl() + { + super.setupTestDataImpl(); + + // Create records + record = utils.createRecord(rmFolder, "testRecord.txt"); + } + + public void testJSON() throws Exception + { + doTestInTransaction(new JSONTest + ( + filePlan, + new String[]{"isRmNode", "true", "boolean"}, + new String[]{"rmNode.kind", "FILE_PLAN"} + ){}); + + doTestInTransaction(new JSONTest + ( + rmContainer, + new String[]{"isRmNode", "true", "boolean"}, + new String[]{"rmNode.kind", "RECORD_CATEGORY"} + ){}); + + doTestInTransaction(new JSONTest + ( + rmFolder, + new String[]{"isRmNode", "true", "boolean"}, + new String[]{"rmNode.kind", "RECORD_FOLDER"}, + new String[]{"rmNode.closed", "false", "boolean"}, + new String[]{"rmNode.declared", "false", "boolean"}, + new String[]{"rmNode.frozen", "false", "boolean"}, + new String[]{"rmNode.metatdata-stub", "false", "boolean"} + //containsFrozen + ){}); + + doTestInTransaction(new JSONTest + ( + record, + new String[]{"isRmNode", "true", "boolean"}, + new String[]{"rmNode.kind", "RECORD"}, + new String[]{"rmNode.declared", "false", "boolean"}, + new String[]{"rmNode.frozen", "false", "boolean"}, + new String[]{"rmNode.metatdata-stub", "false", "boolean"} + ){}); + } + + class JSONTest extends Test + { + private NodeRef nodeRef; + private String[][] testValues; + + public JSONTest(NodeRef nodeRef, String[] ... testValues) + { + this.nodeRef = nodeRef; + this.testValues = testValues; + } + + @Override + public JSONObject run() throws Exception + { + String json = converter.toJSON(nodeRef, true); + System.out.println(json); + return new JSONObject(json); + } + + @Override + public void test(JSONObject result) throws Exception + { + for (String[] testValue : testValues) + { + String key = testValue[0]; + String type = "string"; + if (testValue.length == 3) + { + type = testValue[2]; + } + Serializable value = convertValue(testValue[1], type); + Serializable actualValue = (Serializable)getValue(result, key); + + assertEquals("The key " + key + " did not have the expected value.", value, actualValue); + } + } + + private Serializable convertValue(String stringValue, String type) + { + Serializable value = stringValue; + if (type.equals("boolean") == true) + { + value = new Boolean(stringValue); + } + return value; + } + + private Object getValue(JSONObject jsonObject, String key) throws JSONException + { + return getValue(jsonObject, key.split("\\.")); + } + + private Object getValue(JSONObject jsonObject, String[] key) throws JSONException + { + if (key.length == 1) + { + return jsonObject.get(key[0]); + } + else + { + return getValue(jsonObject.getJSONObject(key[0]), + (String[])ArrayUtils.subarray(key, 1, key.length)); + } + } + } +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/jscript/RMJScriptTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/jscript/RMJScriptTest.java new file mode 100644 index 0000000000..a1326d4680 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/jscript/RMJScriptTest.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.jscript; + +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; +import org.alfresco.repo.jscript.ClasspathScriptLocation; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.ScriptLocation; +import org.alfresco.service.cmr.repository.ScriptService; + +/** + * @author Roy Wetherall + */ +public class RMJScriptTest extends BaseRMTestCase +{ + private static String SCRIPT_PATH = "org/alfresco/module/org_alfresco_module_rm/test/jscript/"; + private static String CAPABILITIES_TEST = "CapabilitiesTest.js"; + + private ScriptService scriptService; + + @Override + protected void initServices() + { + super.initServices(); + this.scriptService = (ScriptService)this.applicationContext.getBean("ScriptService"); + } + + private NodeRef record; + public void testCapabilities() throws Exception + { + doTestInTransaction(new Test() + { + @Override + public Void run() + { + record = utils.createRecord(rmFolder, "testRecord.txt"); + return null; + } + }); + + doTestInTransaction(new Test() + { + @Override + public NodeRef run() + { + utils.declareRecord(record); + return record; + } + + @Override + public void test(NodeRef record) throws Exception + { + + // Create a model to pass to the unit test scripts + Map model = new HashMap(1); + model.put("filePlan", filePlan); + model.put("record", record); + + executeScript(CAPABILITIES_TEST, model); + } + }); + } + + private void executeScript(String script, Map model) + { + // Execute the unit test script + ScriptLocation location = new ClasspathScriptLocation(SCRIPT_PATH + script); + this.scriptService.executeScript(location, model); + } +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/DispositionServiceImplTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/DispositionServiceImplTest.java new file mode 100644 index 0000000000..4eb6cf314d --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/DispositionServiceImplTest.java @@ -0,0 +1,801 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.service; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.module.org_alfresco_module_rm.event.EventCompletionDetails; +import org.alfresco.module.org_alfresco_module_rm.job.publish.PublishExecutor; +import org.alfresco.module.org_alfresco_module_rm.job.publish.PublishExecutorRegistry; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; +import org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * Disposition service implementation unit test. + * + * @author Roy Wetherall + */ +public class DispositionServiceImplTest extends BaseRMTestCase +{ + @Override + protected boolean isMultiHierarchyTest() + { + return true; + } + + /** + * @see DispositionService#getDispositionSchedule(org.alfresco.service.cmr.repository.NodeRef) + */ + public void testGetDispositionSchedule() throws Exception + { + doTestInTransaction(new Test() + { + @Override + public Void run() + { + // Check for null lookup's + assertNull(dispositionService.getDispositionSchedule(filePlan)); + + // Get the containers disposition schedule + DispositionSchedule ds = dispositionService.getDispositionSchedule(rmContainer); + assertNotNull(ds); + checkDispositionSchedule(ds, false); + + // Get the folders disposition schedule + ds = dispositionService.getDispositionSchedule(rmContainer); + assertNotNull(ds); + checkDispositionSchedule(ds, false); + + return null; + } + + }); + + // Failure: Root node + doTestInTransaction(new FailureTest + ( + "Should not be able to get adisposition schedule for the root node", + AlfrescoRuntimeException.class + ) + { + @Override + public void run() + { + dispositionService.getDispositionSchedule(rootNodeRef); + } + }); + + // Failure: Non-rm node + doTestInTransaction(new FailureTest() + { + @Override + public void run() + { + dispositionService.getDispositionSchedule(folder); + } + }); + } + + /** + * @see DispositionService#getDispositionSchedule(org.alfresco.service.cmr.repository.NodeRef) + */ + public void testGetDispositionScheduleMultiHier() throws Exception + { + doTestInTransaction(new Test() + { + @Override + public Void run() + { + assertNull(dispositionService.getDispositionSchedule(mhContainer)); + + // Level 1 + doCheck(mhContainer11, "ds11", false); + doCheck(mhContainer12, "ds12", false); + + // Level 2 + doCheck(mhContainer21, "ds11", false); + doCheck(mhContainer22, "ds12", false); + doCheck(mhContainer23, "ds23", false); + + // Level 3 + doCheck(mhContainer31, "ds11", false); + doCheck(mhContainer32, "ds12", false); + doCheck(mhContainer33, "ds33", true); + doCheck(mhContainer34, "ds23", false); + doCheck(mhContainer35, "ds35", true); + + // Folders + doCheckFolder(mhRecordFolder41, "ds11", false); + doCheckFolder(mhRecordFolder42, "ds12", false); + doCheckFolder(mhRecordFolder43, "ds33", true); + doCheckFolder(mhRecordFolder44, "ds23", false); + doCheckFolder(mhRecordFolder45, "ds35", true); + + return null; + } + + private void doCheck(NodeRef container, String dispositionInstructions, boolean isRecordLevel) + { + DispositionSchedule ds = dispositionService.getDispositionSchedule(container); + assertNotNull(ds); + checkDispositionSchedule(ds, dispositionInstructions, CommonRMTestUtils.DEFAULT_DISPOSITION_AUTHORITY, isRecordLevel); + } + + private void doCheckFolder(NodeRef container, String dispositionInstructions, boolean isRecordLevel) + { + doCheck(container, dispositionInstructions, isRecordLevel); + if (isRecordLevel == false) + { + assertNotNull(dispositionService.getNextDispositionAction(container)); + } + } + }); + + } + + /** + * Checks a disposition schedule + * + * @param ds disposition scheduleS + */ + private void checkDispositionSchedule(DispositionSchedule ds, String dispositionInstructions, String dispositionAuthority, boolean isRecordLevel) + { + assertEquals(dispositionAuthority, ds.getDispositionAuthority()); + assertEquals(dispositionInstructions, ds.getDispositionInstructions()); + assertEquals(isRecordLevel, ds.isRecordLevelDisposition()); + + List defs = ds.getDispositionActionDefinitions(); + assertNotNull(defs); + assertEquals(2, defs.size()); + + DispositionActionDefinition defCutoff = ds.getDispositionActionDefinitionByName("cutoff"); + assertNotNull(defCutoff); + assertEquals("cutoff", defCutoff.getName()); + + DispositionActionDefinition defDestroy = ds.getDispositionActionDefinitionByName("destroy"); + assertNotNull(defDestroy); + assertEquals("destroy", defDestroy.getName()); + } + + /** + * + * @param ds + */ + private void checkDispositionSchedule(DispositionSchedule ds, boolean isRecordLevel) + { + checkDispositionSchedule(ds, CommonRMTestUtils.DEFAULT_DISPOSITION_INSTRUCTIONS, CommonRMTestUtils.DEFAULT_DISPOSITION_AUTHORITY, isRecordLevel); + } + + /** + * @see DispositionService#getAssociatedDispositionSchedule(NodeRef) + */ + public void testGetAssociatedDispositionSchedule() throws Exception + { + // Get associated disposition schedule for rmContainer + doTestInTransaction(new Test() + { + @Override + public Void run() + { + // Get the containers disposition schedule + DispositionSchedule ds = dispositionService.getAssociatedDispositionSchedule(rmContainer); + assertNotNull(ds); + checkDispositionSchedule(ds, false); + + // Show the null disposition schedules + assertNull(dispositionService.getAssociatedDispositionSchedule(filePlan)); + assertNull(dispositionService.getAssociatedDispositionSchedule(rmFolder)); + + return null; + } + }); + + // Failure: associated disposition schedule for non-rm node + doTestInTransaction(new FailureTest() + { + @Override + public void run() + { + dispositionService.getAssociatedDispositionSchedule(folder); + } + }); + } + + /** + * @see DispositionService#getAssociatedDispositionSchedule(NodeRef) + */ + public void testGetAssociatedDispositionScheduleMultiHier() throws Exception + { + doTestInTransaction(new Test() + { + @Override + public Void run() + { + assertNull(dispositionService.getAssociatedDispositionSchedule(mhContainer)); + + // Level 1 + doCheck(mhContainer11, "ds11", false); + doCheck(mhContainer12, "ds12", false); + + // Level 2 + assertNull(dispositionService.getAssociatedDispositionSchedule(mhContainer21)); + assertNull(dispositionService.getAssociatedDispositionSchedule(mhContainer22)); + doCheck(mhContainer23, "ds23", false); + + // Level 3 + assertNull(dispositionService.getAssociatedDispositionSchedule(mhContainer31)); + assertNull(dispositionService.getAssociatedDispositionSchedule(mhContainer32)); + doCheck(mhContainer33, "ds33", true); + assertNull(dispositionService.getAssociatedDispositionSchedule(mhContainer34)); + doCheck(mhContainer35, "ds35", true); + + return null; + } + + private void doCheck(NodeRef container, String dispositionInstructions, boolean isRecordLevel) + { + DispositionSchedule ds = dispositionService.getAssociatedDispositionSchedule(container); + assertNotNull(ds); + checkDispositionSchedule(ds, dispositionInstructions, CommonRMTestUtils.DEFAULT_DISPOSITION_AUTHORITY, isRecordLevel); + } + }); + } + + /** + * @see DispositionService#hasDisposableItems(DispositionSchedule) + */ + public void testHasDisposableItems() throws Exception + { + doTestInTransaction(new Test() + { + @Override + public Void run() + { + // Add a new disposition schedule + NodeRef container = rmService.createRecordCategory(rmContainer, "hasDisposableTest"); + DispositionSchedule ds = utils.createBasicDispositionSchedule(container); + + assertTrue(dispositionService.hasDisposableItems(dispositionSchedule)); + assertFalse(dispositionService.hasDisposableItems(ds)); + + return null; + } + }); + } + + /** + * @see DispositionService#hasDisposableItems(DispositionSchedule) + */ + public void testHasDisposableItemsMultiHier() throws Exception + { + doTestInTransaction(new Test() + { + @Override + public Void run() + { + assertTrue(dispositionService.hasDisposableItems(mhDispositionSchedule11)); + assertTrue(dispositionService.hasDisposableItems(mhDispositionSchedule12)); + assertTrue(dispositionService.hasDisposableItems(mhDispositionSchedule23)); + assertFalse(dispositionService.hasDisposableItems(mhDispositionSchedule33)); + assertFalse(dispositionService.hasDisposableItems(mhDispositionSchedule35)); + + return null; + } + }); + } + + /** + * @see DispositionService#getDisposableItems(DispositionSchedule) + */ + public void testGetDisposableItems() throws Exception + { + doTestInTransaction(new Test() + { + @Override + public Void run() + { + List nodeRefs = dispositionService.getDisposableItems(dispositionSchedule); + assertNotNull(nodeRefs); + assertEquals(1, nodeRefs.size()); + assertTrue(nodeRefs.contains(rmFolder)); + + return null; + } + }); + } + + /** + * @see DispositionService#getDisposableItems(DispositionSchedule) + */ + public void testGetDisposableItemsMultiHier() throws Exception + { + doTestInTransaction(new Test() + { + @Override + public Void run() + { + List nodeRefs = dispositionService.getDisposableItems(mhDispositionSchedule11); + assertNotNull(nodeRefs); + assertEquals(1, nodeRefs.size()); + assertTrue(nodeRefs.contains(mhRecordFolder41)); + + nodeRefs = dispositionService.getDisposableItems(mhDispositionSchedule12); + assertNotNull(nodeRefs); + assertEquals(1, nodeRefs.size()); + assertTrue(nodeRefs.contains(mhRecordFolder42)); + + nodeRefs = dispositionService.getDisposableItems(mhDispositionSchedule23); + assertNotNull(nodeRefs); + assertEquals(1, nodeRefs.size()); + assertTrue(nodeRefs.contains(mhRecordFolder44)); + + nodeRefs = dispositionService.getDisposableItems(mhDispositionSchedule33); + assertNotNull(nodeRefs); + assertEquals(0, nodeRefs.size()); + + nodeRefs = dispositionService.getDisposableItems(mhDispositionSchedule35); + assertNotNull(nodeRefs); + assertEquals(0, nodeRefs.size()); + + return null; + } + }); + } + + /** + * @see DispositionService#createDispositionSchedule(NodeRef, Map) + */ + public void testCreateDispositionSchedule() throws Exception + { + // Test: simple disposition create + doTestInTransaction(new Test() + { + @Override + public NodeRef run() + { + // Create a new container + NodeRef container = rmService.createRecordCategory(filePlan, "testCreateDispositionSchedule"); + + // Create a new disposition schedule + utils.createBasicDispositionSchedule(container, "testCreateDispositionSchedule", "testCreateDispositionSchedule", false, true); + + return container; + } + + @Override + public void test(NodeRef result) throws Exception + { + // Get the created disposition schedule + DispositionSchedule ds = dispositionService.getAssociatedDispositionSchedule(result); + assertNotNull(ds); + + // Check the disposition schedule + checkDispositionSchedule(ds, "testCreateDispositionSchedule", "testCreateDispositionSchedule", false); + } + }); + + // Failure: create disposition schedule on container with existing disposition schedule + doTestInTransaction(new FailureTest + ( + "Can not create a disposition schedule on a container with an existing disposition schedule" + ) + { + @Override + public void run() + { + utils.createBasicDispositionSchedule(rmContainer); + } + }); + } + + /** + * @see DispositionService#createDispositionSchedule(NodeRef, Map) + */ + public void testCreateDispositionScheduleMultiHier() throws Exception + { + // Test: simple disposition create + doTestInTransaction(new Test() + { + @Override + public Void run() + { + // Create a new structure container + NodeRef testA = rmService.createRecordCategory(mhContainer, "testA"); + NodeRef testB = rmService.createRecordCategory(testA, "testB"); + + // Create new disposition schedules + utils.createBasicDispositionSchedule(testA, "testA", "testA", false, true); + utils.createBasicDispositionSchedule(testB, "testB", "testB", false, true); + + // Add created containers to model + setNodeRef("testA", testA); + setNodeRef("testB", testB); + + return null; + } + + @Override + public void test(Void result) throws Exception + { + // Get the created disposition schedule + DispositionSchedule testA = dispositionService.getAssociatedDispositionSchedule(getNodeRef("testA")); + assertNotNull(testA); + DispositionSchedule testB = dispositionService.getAssociatedDispositionSchedule(getNodeRef("testB")); + assertNotNull(testB); + + // Check the disposition schedule + checkDispositionSchedule(testA, "testA", "testA", false); + checkDispositionSchedule(testB, "testB", "testB", false); + } + }); + + // Failure: create disposition schedule on container with existing disposition schedule + doTestInTransaction(new FailureTest + ( + "Can not create a disposition schedule on container with an existing disposition schedule" + ) + { + @Override + public void run() + { + utils.createBasicDispositionSchedule(mhContainer11); + } + }); + + // Failure: create disposition schedule on a container where there are disposable items under management + doTestInTransaction(new FailureTest + ( + "Can not create a disposition schedule on a container where there are already disposable items under management" + ) + { + @Override + public void run() + { + utils.createBasicDispositionSchedule(mhContainer21); + } + }); + } + + /** + * @see DispositionService#getAssociatedRecordsManagementContainer(DispositionSchedule) + */ + public void testGetAssociatedRecordsManagementContainer() throws Exception + { + doTestInTransaction(new Test() + { + @Override + public Void run() + { + NodeRef nodeRef = dispositionService.getAssociatedRecordsManagementContainer(dispositionSchedule); + assertNotNull(nodeRef); + assertEquals(rmContainer, nodeRef); + + return null; + } + }); + } + + /** + * @see DispositionService#getAssociatedRecordsManagementContainer(DispositionSchedule) + */ + public void testGetAssociatedRecordsManagementContainerMultiHier() throws Exception + { + doTestInTransaction(new Test() + { + @Override + public Void run() + { + NodeRef nodeRef = dispositionService.getAssociatedRecordsManagementContainer(mhDispositionSchedule11); + assertNotNull(nodeRef); + assertEquals(mhContainer11, nodeRef); + + nodeRef = dispositionService.getAssociatedRecordsManagementContainer(mhDispositionSchedule12); + assertNotNull(nodeRef); + assertEquals(mhContainer12, nodeRef); + + nodeRef = dispositionService.getAssociatedRecordsManagementContainer(mhDispositionSchedule23); + assertNotNull(nodeRef); + assertEquals(mhContainer23, nodeRef); + + nodeRef = dispositionService.getAssociatedRecordsManagementContainer(mhDispositionSchedule33); + assertNotNull(nodeRef); + assertEquals(mhContainer33, nodeRef); + + nodeRef = dispositionService.getAssociatedRecordsManagementContainer(mhDispositionSchedule35); + assertNotNull(nodeRef); + assertEquals(mhContainer35, nodeRef); + + return null; + } + }); + } + + // TODO DispositionActionDefinition addDispositionActionDefinition + + // TODO void removeDispositionActionDefinition( + + private NodeRef record43; + private NodeRef record45; + + public void testUpdateDispositionActionDefinitionMultiHier() throws Exception + { + doTestInTransaction(new Test() + { + @Override + public Void run() throws Exception + { + record43 = utils.createRecord(mhRecordFolder43, "record1.txt"); + record45 = utils.createRecord(mhRecordFolder45, "record2.txt"); + + return null; + } + }); + + doTestInTransaction(new Test() + { + @Override + public Void run() throws Exception + { + // Check all the current record folders first + checkDisposableItemUnchanged(mhRecordFolder41); + checkDisposableItemUnchanged(mhRecordFolder42); + checkDisposableItemUnchanged(record43); + checkDisposableItemUnchanged(mhRecordFolder44); + checkDisposableItemUnchanged(record45); + + updateDispositionScheduleOnContainer(mhContainer11); + + return null; + } + + @Override + public void test(Void result) throws Exception + { + // Check all the current record folders first + checkDisposableItemChanged(mhRecordFolder41); + checkDisposableItemUnchanged(mhRecordFolder42); + checkDisposableItemUnchanged(record43); + checkDisposableItemUnchanged(mhRecordFolder44); + checkDisposableItemUnchanged(record45);; + } + }); + + doTestInTransaction(new Test() + { + @Override + public Void run() throws Exception + { + updateDispositionScheduleOnContainer(mhContainer12); + + return null; + } + + @Override + public void test(Void result) throws Exception + { + // Check all the current record folders first + checkDisposableItemChanged(mhRecordFolder41); + checkDisposableItemChanged(mhRecordFolder42); + checkDisposableItemUnchanged(record43); + checkDisposableItemUnchanged(mhRecordFolder44); + checkDisposableItemUnchanged(record45);; + } + }); + + doTestInTransaction(new Test() + { + @Override + public Void run() throws Exception + { + updateDispositionScheduleOnContainer(mhContainer33); + + return null; + } + + @Override + public void test(Void result) throws Exception + { + // Check all the current record folders first + checkDisposableItemChanged(mhRecordFolder41); + checkDisposableItemChanged(mhRecordFolder42); + checkDisposableItemChanged(record43); + checkDisposableItemUnchanged(mhRecordFolder44); + checkDisposableItemUnchanged(record45);; + } + }); + + doTestInTransaction(new Test() + { + @Override + public Void run() throws Exception + { + updateDispositionScheduleOnContainer(mhContainer23); + + return null; + } + + @Override + public void test(Void result) throws Exception + { + // Check all the current record folders first + checkDisposableItemChanged(mhRecordFolder41); + checkDisposableItemChanged(mhRecordFolder42); + checkDisposableItemChanged(record43); + checkDisposableItemChanged(mhRecordFolder44); + checkDisposableItemUnchanged(record45); + } + }); + + doTestInTransaction(new Test() + { + @Override + public Void run() throws Exception + { + updateDispositionScheduleOnContainer(mhContainer35); + + return null; + } + + @Override + public void test(Void result) throws Exception + { + // Check all the current record folders first + checkDisposableItemChanged(mhRecordFolder41); + checkDisposableItemChanged(mhRecordFolder42); + checkDisposableItemChanged(record43); + checkDisposableItemChanged(mhRecordFolder44); + checkDisposableItemChanged(record45); + } + }); + } + + private void publishDispositionActionDefinitionChange(DispositionActionDefinition dad) + { + PublishExecutorRegistry reg = (PublishExecutorRegistry)applicationContext.getBean("publishExecutorRegistry"); + PublishExecutor pub = reg.get(RecordsManagementModel.UPDATE_TO_DISPOSITION_ACTION_DEFINITION); + assertNotNull(pub); + pub.publish(dad.getNodeRef()); + } + + private void checkDisposableItemUnchanged(NodeRef recordFolder) + { + checkDispositionAction( + dispositionService.getNextDispositionAction(recordFolder), + "cutoff", + new String[]{CommonRMTestUtils.DEFAULT_EVENT_NAME}, + CommonRMTestUtils.PERIOD_NONE); + } + + private void checkDisposableItemChanged(NodeRef recordFolder) throws Exception + { + checkDispositionAction( + dispositionService.getNextDispositionAction(recordFolder), + "cutoff", + new String[]{CommonRMTestUtils.DEFAULT_EVENT_NAME, "abolished"}, + "week|1"); + } + + private void updateDispositionScheduleOnContainer(NodeRef nodeRef) + { + Map updateProps = new HashMap(3); + updateProps.put(PROP_DISPOSITION_PERIOD, "week|1"); + updateProps.put(PROP_DISPOSITION_EVENT, (Serializable)Arrays.asList(CommonRMTestUtils.DEFAULT_EVENT_NAME, "abolished")); + + DispositionSchedule ds = dispositionService.getDispositionSchedule(nodeRef); + DispositionActionDefinition dad = ds.getDispositionActionDefinitionByName("cutoff"); + dispositionService.updateDispositionActionDefinition(dad, updateProps); + publishDispositionActionDefinitionChange(dad); + } + + /** + * + * @param da + * @param name + * @param arrEventNames + * @param strPeriod + */ + private void checkDispositionAction(DispositionAction da, String name, String[] arrEventNames, String strPeriod) + { + assertNotNull(da); + assertEquals(name, da.getName()); + + List events = da.getEventCompletionDetails(); + assertNotNull(events); + assertEquals(arrEventNames.length, events.size()); + + List origEvents = new ArrayList(events.size()); + for (EventCompletionDetails event : events) + { + origEvents.add(event.getEventName()); + } + + List expectedEvents = Arrays.asList(arrEventNames); + Collection copy = new ArrayList(origEvents); + + for (Iterator i = origEvents.iterator(); i.hasNext(); ) + { + String origEvent = i.next(); + + if (expectedEvents.contains(origEvent) == true) + { + i.remove(); + copy.remove(origEvent); + } + } + + if (copy.size() != 0 && expectedEvents.size() != 0) + { + StringBuffer buff = new StringBuffer(255); + if (copy.size() != 0) + { + buff.append("The following events where found, but not expected: ("); + for (String eventName : copy) + { + buff.append(eventName).append(", "); + } + buff.append("). "); + } + if (expectedEvents.size() != 0) + { + buff.append("The following events where not found, but expected: ("); + for (String eventName : expectedEvents) + { + buff.append(eventName).append(", "); + } + buff.append(")."); + } + fail(buff.toString()); + } + + if (CommonRMTestUtils.PERIOD_NONE.equals(strPeriod) == true) + { + assertNull(da.getAsOfDate()); + } + else + { + assertNotNull(da.getAsOfDate()); + } + } + + // TODO boolean isNextDispositionActionEligible(NodeRef nodeRef); + + // TODO DispositionAction getNextDispositionAction(NodeRef nodeRef); + + // TODO List getCompletedDispositionActions(NodeRef nodeRef); + + // TODO DispositionAction getLastCompletedDispostionAction(NodeRef nodeRef); + + // TODO List getDispositionPeriodProperties(); + +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RMCaveatConfigServiceImplTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RMCaveatConfigServiceImplTest.java new file mode 100644 index 0000000000..e36de6ef56 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RMCaveatConfigServiceImplTest.java @@ -0,0 +1,612 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.service; + +import java.util.ArrayList; +import java.util.List; + +import javax.transaction.UserTransaction; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.caveat.RMCaveatConfigService; +import org.alfresco.module.org_alfresco_module_rm.caveat.RMCaveatConfigServiceImpl; +import org.alfresco.module.org_alfresco_module_rm.dod5015.DOD5015Model; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.BaseSpringTest; +import org.alfresco.util.PropertyMap; + +/** + * Test of RM Caveat (Admin facing scripts) + * + * @author Mark Rogers + */ +public class RMCaveatConfigServiceImplTest extends BaseSpringTest implements DOD5015Model +{ + protected static StoreRef SPACES_STORE = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore"); + + private NodeRef filePlan; + + private NodeService nodeService; + private TransactionService transactionService; + private RMCaveatConfigService caveatConfigService; + + private MutableAuthenticationService authenticationService; + private PersonService personService; + private AuthorityService authorityService; + + + // example base test data for supplemental markings list + protected final static String NOFORN = "NOFORN"; // Not Releasable to Foreign Nationals/Governments/Non-US Citizens + protected final static String NOCONTRACT = "NOCONTRACT"; // Not Releasable to Contractors or Contractor/Consultants + protected final static String FOUO = "FOUO"; // For Official Use Only + protected final static String FGI = "FGI"; // Foreign Government Information + + protected final static String RM_LIST = "rmc:smList"; // existing pre-defined list + protected final static String RM_LIST_ALT = "rmc:anoList"; + + @Override + protected void onSetUpInTransaction() throws Exception + { + super.onSetUpInTransaction(); + + // Get the service required in the tests + this.nodeService = (NodeService)this.applicationContext.getBean("NodeService"); // use upper 'N'odeService (to test access config interceptor) + this.authenticationService = (MutableAuthenticationService)this.applicationContext.getBean("AuthenticationService"); + this.personService = (PersonService)this.applicationContext.getBean("PersonService"); + this.authorityService = (AuthorityService)this.applicationContext.getBean("AuthorityService"); + this.caveatConfigService = (RMCaveatConfigServiceImpl)this.applicationContext.getBean("caveatConfigService"); + this.transactionService = (TransactionService)this.applicationContext.getBean("TransactionService"); + + + // Set the current security context as admin + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + + // Get the test data + setUpTestData(); + } + + private void setUpTestData() + { + } + + @Override + protected void onTearDownInTransaction() throws Exception + { + try + { + UserTransaction txn = transactionService.getUserTransaction(false); + txn.begin(); + this.nodeService.deleteNode(filePlan); + txn.commit(); + } + catch (Exception e) + { + // Nothing + //System.out.println("DID NOT DELETE FILE PLAN!"); + } + } + + @Override + protected void onTearDownAfterTransaction() throws Exception + { + // TODO Auto-generated method stub + super.onTearDownAfterTransaction(); + } + + public void testSetup() + { + // NOOP + } + + + /** + * Test of Caveat Config + * + * @throws Exception + */ + public void testAddRMConstraintList() throws Exception + { + setComplete(); + endTransaction(); + + cleanCaveatConfigData(); + + startNewTransaction(); + + /** + * Now remove the entire list (rma:smList); + */ + logger.debug("test remove entire list rmc:smList"); + caveatConfigService.deleteRMConstraint(RM_LIST); + + /** + * Now add the list again + */ + logger.debug("test add back rmc:smList"); + caveatConfigService.addRMConstraint(RM_LIST, "my title", new String[0]); + + /** + * Negative test - add a list that already exists + */ + logger.debug("try to create duplicate list rmc:smList"); + caveatConfigService.addRMConstraint(RM_LIST, "my title", new String[0]); + + /** + * Negative test - remove a list that does not exist + */ + logger.debug("test remove entire list rmc:smList"); + caveatConfigService.deleteRMConstraint(RM_LIST); + try + { + caveatConfigService.deleteRMConstraint(RM_LIST); + fail("unknown constraint should have thrown an exception"); + } + catch (Exception e) + { + // expect to go here + } + + + /** + * Negative test - add a constraint to property that does not exist + */ + logger.debug("test property does not exist"); + try + { + caveatConfigService.addRMConstraint("rma:mer", "", new String[0]); + fail("unknown property should have thrown an exception"); + } + catch (Exception e) + { + // expect to go here + } + endTransaction(); + cleanCaveatConfigData(); + + } + + /** + * Test of addRMConstraintListValue + * + * @throws Exception + */ + public void testAddRMConstraintListValue() throws Exception + { + setComplete(); + endTransaction(); + + cleanCaveatConfigData(); + setupCaveatConfigData(); + + startNewTransaction(); + caveatConfigService.addRMConstraint(RM_LIST, "my title", new String[0]); + + /** + * Add a user to the list + */ + List values = new ArrayList(); + values.add(NOFORN); + values.add(NOCONTRACT); + caveatConfigService.updateRMConstraintListAuthority(RM_LIST, "jrogers", values); + + /** + * Add another value to that list + */ + caveatConfigService.addRMConstraintListValue(RM_LIST, "jrogers", FGI); + + /** + * Negative test - attempt to add a duplicate value + */ + caveatConfigService.addRMConstraintListValue(RM_LIST, "jrogers", FGI); + + /** + * Negative test - attempt to add to a list that does not exist + */ + try + { + caveatConfigService.addRMConstraintListValue(RM_LIST_ALT, "mhouse", FGI); + fail("exception not thrown"); + } + catch (Exception re) + { + // should go here + + } + + /** + * Negative test - attempt to add to a list that does exist and user that does not exist + */ + try + { + caveatConfigService.addRMConstraintListValue(RM_LIST, "mhouse", FGI); + fail("exception not thrown"); + } + catch (Exception e) + { + // should go here + } + + } + + + /** + * Test of UpdateRMConstraintListAuthority + * + * @throws Exception + */ + public void testUpdateRMConstraintListAuthority() throws Exception + { + setComplete(); + endTransaction(); + + cleanCaveatConfigData(); + setupCaveatConfigData(); + + startNewTransaction(); + + caveatConfigService.addRMConstraint(RM_LIST, "my title", new String[0]); + + /** + * Add a user to the list + */ + List values = new ArrayList(); + values.add(NOFORN); + values.add(NOCONTRACT); + caveatConfigService.updateRMConstraintListAuthority(RM_LIST, "jrogers", values); + + /** + * Add to a authority that already exists + * Should replace existing authority + */ + List updatedValues = new ArrayList(); + values.add(FGI); + caveatConfigService.updateRMConstraintListAuthority(RM_LIST, "jrogers", updatedValues); + + /** + * Add a group to the list + */ + caveatConfigService.updateRMConstraintListAuthority(RM_LIST, "Engineering", values); + + /** + * Add to a list that does not exist + * Should create a new list + */ + caveatConfigService.deleteRMConstraint(RM_LIST); + caveatConfigService.updateRMConstraintListAuthority(RM_LIST, "jrogers", values); + + + /** + * Add to a authority that already exists + * Should replace existing authority + */ + + endTransaction(); + cleanCaveatConfigData(); + + } + + /** + * Test of RemoveRMConstraintListAuthority + * + * @throws Exception + */ + public void testRemoveRMConstraintListAuthority() throws Exception + { + setComplete(); + endTransaction(); + + cleanCaveatConfigData(); + setupCaveatConfigData(); + + startNewTransaction(); + caveatConfigService.addRMConstraint(RM_LIST, "my title", new String[0]); + + List values = new ArrayList(); + values.add(FGI); + caveatConfigService.updateRMConstraintListAuthority(RM_LIST, "jrogers", values); + + /** + * Remove a user from a list + */ + caveatConfigService.removeRMConstraintListAuthority(RM_LIST, "jrogers"); + + /** + * Negative test - remove a user that does not exist + */ + caveatConfigService.removeRMConstraintListAuthority(RM_LIST, "jrogers"); + + /** + * Negative test - remove a user from a list that does not exist. + * Should create a new list + */ + + caveatConfigService.addRMConstraint(RM_LIST, "my title", new String[0]); + caveatConfigService.updateRMConstraintListAuthority(RM_LIST, "jrogers", values); + + endTransaction(); + cleanCaveatConfigData(); + + } + + + + + /** + * Test of Caveat Config + * + * @throws Exception + */ + public void testRMCaveatConfig() throws Exception + { + setComplete(); + endTransaction(); + + cleanCaveatConfigData(); + + startNewTransaction(); + + caveatConfigService.addRMConstraint(RM_LIST, "my title", new String[0]); + + List values = new ArrayList(); + values.add(NOFORN); + values.add(FOUO); + caveatConfigService.updateRMConstraintListAuthority(RM_LIST, "dfranco", values); + + values.add(FGI); + values.add(NOCONTRACT); + caveatConfigService.updateRMConstraintListAuthority(RM_LIST, "dmartinz", values); + + // Test list of allowed values for caveats + + List allowedValues = AuthenticationUtil.runAs(new RunAsWork>() + { + public List doWork() + { + // get allowed values for given caveat (for current user) + return caveatConfigService.getRMAllowedValues(RM_LIST); + } + }, "dfranco"); + + assertEquals(2, allowedValues.size()); + assertTrue(allowedValues.contains(NOFORN)); + assertTrue(allowedValues.contains(FOUO)); + + + allowedValues = AuthenticationUtil.runAs(new RunAsWork>() + { + public List doWork() + { + // get allowed values for given caveat (for current user) + return caveatConfigService.getRMAllowedValues(RM_LIST); + } + }, "dmartinz"); + + assertEquals(4, allowedValues.size()); + assertTrue(allowedValues.contains(NOFORN)); + assertTrue(allowedValues.contains(NOCONTRACT)); + assertTrue(allowedValues.contains(FOUO)); + assertTrue(allowedValues.contains(FGI)); + + /** + // + * Now remove the entire list (rma:smList); + */ + logger.debug("test remove entire list rmc:smList"); + caveatConfigService.deleteRMConstraint(RM_LIST); + + + /** + * Now add the list again + */ + logger.debug("test add back rmc:smList"); + caveatConfigService.addRMConstraint(RM_LIST, "my title", new String[0]); + + /** + * Negative test - add a list that already exists + */ + logger.debug("try to create duplicate list rmc:smList"); + caveatConfigService.addRMConstraint(RM_LIST, "my title", new String[0]); + + /** + * Negative test - remove a list that does not exist + */ + logger.debug("test remove entire list rmc:smList"); + caveatConfigService.deleteRMConstraint(RM_LIST); + try + { + caveatConfigService.deleteRMConstraint(RM_LIST); + fail("unknown constraint should have thrown an exception"); + } + catch (Exception e) + { + // expect to go here + } + + + /** + * Negative test - add a constraint to property that does not exist + */ + logger.debug("test property does not exist"); + try + { + caveatConfigService.addRMConstraint("rma:mer", "", new String[0]); + fail("unknown property should have thrown an exception"); + } + catch (Exception e) + { + // expect to go here + } + endTransaction(); + cleanCaveatConfigData(); + } + + private void cleanCaveatConfigData() + { + startNewTransaction(); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + + deleteUser("jrangel"); + deleteUser("dmartinz"); + deleteUser("jrogers"); + deleteUser("hmcneil"); + deleteUser("dfranco"); + deleteUser("gsmith"); + deleteUser("eharris"); + deleteUser("bbayless"); + deleteUser("mhouse"); + deleteUser("aly"); + deleteUser("dsandy"); + deleteUser("driggs"); + deleteUser("test1"); + + deleteGroup("Engineering"); + deleteGroup("Finance"); + deleteGroup("test1"); + + caveatConfigService.updateOrCreateCaveatConfig("{}"); // empty config ! + + setComplete(); + endTransaction(); + } + + private void setupCaveatConfigData() + { + startNewTransaction(); + + // Switch to admin + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + + // Create test users/groups (if they do not already exist) + + createUser("jrangel"); + createUser("dmartinz"); + createUser("jrogers"); + createUser("hmcneil"); + createUser("dfranco"); + createUser("gsmith"); + createUser("eharris"); + createUser("bbayless"); + createUser("mhouse"); + createUser("aly"); + createUser("dsandy"); + createUser("driggs"); + createUser("test1"); + + createGroup("Engineering"); + createGroup("Finance"); + createGroup("test1"); + + addToGroup("jrogers", "Engineering"); + addToGroup("dfranco", "Finance"); + + // not in grouo to start with - added later + //addToGroup("gsmith", "Engineering"); + + + //URL url = AbstractContentTransformerTest.class.getClassLoader().getResource("testCaveatConfig2.json"); // from test-resources + //assertNotNull(url); + //File file = new File(url.getFile()); + //assertTrue(file.exists()); + + //caveatConfigService.updateOrCreateCaveatConfig(file); + + setComplete(); + endTransaction(); + } + + protected void createUser(String userName) + { + if (! authenticationService.authenticationExists(userName)) + { + authenticationService.createAuthentication(userName, "PWD".toCharArray()); + } + + if (! personService.personExists(userName)) + { + PropertyMap ppOne = new PropertyMap(4); + ppOne.put(ContentModel.PROP_USERNAME, userName); + ppOne.put(ContentModel.PROP_FIRSTNAME, "firstName"); + ppOne.put(ContentModel.PROP_LASTNAME, "lastName"); + ppOne.put(ContentModel.PROP_EMAIL, "email@email.com"); + ppOne.put(ContentModel.PROP_JOBTITLE, "jobTitle"); + + personService.createPerson(ppOne); + } + } + + protected void deleteUser(String userName) + { + if (personService.personExists(userName)) + { + personService.deletePerson(userName); + } + } + + protected void createGroup(String groupShortName) + { + createGroup(null, groupShortName); + } + + protected void createGroup(String parentGroupShortName, String groupShortName) + { + if (parentGroupShortName != null) + { + String parentGroupFullName = authorityService.getName(AuthorityType.GROUP, parentGroupShortName); + if (authorityService.authorityExists(parentGroupFullName) == false) + { + authorityService.createAuthority(AuthorityType.GROUP, groupShortName, groupShortName, null); + authorityService.addAuthority(parentGroupFullName, groupShortName); + } + } + else + { + authorityService.createAuthority(AuthorityType.GROUP, groupShortName, groupShortName, null); + } + } + + protected void deleteGroup(String groupShortName) + { + String groupFullName = authorityService.getName(AuthorityType.GROUP, groupShortName); + if (authorityService.authorityExists(groupFullName) == true) + { + authorityService.deleteAuthority(groupFullName); + } + } + + protected void addToGroup(String authorityName, String groupShortName) + { + authorityService.addAuthority(authorityService.getName(AuthorityType.GROUP, groupShortName), authorityName); + } + + protected void removeFromGroup(String authorityName, String groupShortName) + { + authorityService.removeAuthority(authorityService.getName(AuthorityType.GROUP, groupShortName), authorityName); + } + +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementActionServiceImplTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementActionServiceImplTest.java new file mode 100644 index 0000000000..8a058abbc3 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementActionServiceImplTest.java @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.service; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import junit.framework.TestCase; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.BeforeRMActionExecution; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.OnRMActionExecution; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.test.util.TestAction; +import org.alfresco.module.org_alfresco_module_rm.test.util.TestAction2; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.policy.Behaviour.NotificationFrequency; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.ApplicationContextHelper; +import org.springframework.context.ApplicationContext; + +/** + * Records management action service implementation test + * + * @author Roy Wetherall + */ +public class RecordsManagementActionServiceImplTest extends TestCase + implements RecordsManagementModel, + BeforeRMActionExecution, + OnRMActionExecution +{ + private static final String[] CONFIG_LOCATIONS = new String[] { + "classpath:alfresco/application-context.xml", + "classpath:test-context.xml"}; + + private ApplicationContext ctx; + + private ServiceRegistry serviceRegistry; + private TransactionService transactionService; + private RetryingTransactionHelper txnHelper; + private NodeService nodeService; + private RecordsManagementActionService rmActionService; + private PolicyComponent policyComponent; + + private NodeRef nodeRef; + private List nodeRefs; + + private boolean beforeMarker; + private boolean onMarker; + private boolean inTest; + + @Override + protected void setUp() throws Exception + { + ctx = ApplicationContextHelper.getApplicationContext(CONFIG_LOCATIONS); + + this.serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY); + this.transactionService = serviceRegistry.getTransactionService(); + this.txnHelper = transactionService.getRetryingTransactionHelper(); + this.nodeService = serviceRegistry.getNodeService(); + + this.rmActionService = (RecordsManagementActionService)ctx.getBean("RecordsManagementActionService"); + this.policyComponent = (PolicyComponent)ctx.getBean("policyComponent"); + + // Set the current security context as admin + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + + RetryingTransactionCallback setUpCallback = new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + // Create a node we can use for the tests + NodeRef rootNodeRef = nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); + nodeRef = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName(NamespaceService.CONTENT_MODEL_PREFIX, "temp.txt"), + ContentModel.TYPE_CONTENT).getChildRef(); + + // Create nodeRef list + nodeRefs = new ArrayList(5); + for (int i = 0; i < 5; i++) + { + nodeRefs.add( + nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName(NamespaceService.CONTENT_MODEL_PREFIX, "temp.txt"), + ContentModel.TYPE_CONTENT).getChildRef()); + } + return null; + } + }; + txnHelper.doInTransaction(setUpCallback); + + beforeMarker = false; + onMarker = false; + inTest = false; + } + + @Override + protected void tearDown() + { + AuthenticationUtil.clearCurrentSecurityContext(); + } + + public void testGetActions() + { + RetryingTransactionCallback testCallback = new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + getActionsImpl(); + return null; + } + }; + txnHelper.doInTransaction(testCallback); + } + + private void getActionsImpl() + { + List result = this.rmActionService.getRecordsManagementActions(); + assertNotNull(result); + Map resultMap = new HashMap(8); + for (RecordsManagementAction action : result) + { + resultMap.put(action.getName(), action); + } + + assertTrue(resultMap.containsKey(TestAction.NAME)); + assertTrue(resultMap.containsKey(TestAction2.NAME)); + + result = this.rmActionService.getDispositionActions(); + resultMap = new HashMap(8); + for (RecordsManagementAction action : result) + { + resultMap.put(action.getName(), action); + } + assertTrue(resultMap.containsKey(TestAction.NAME)); + assertFalse(resultMap.containsKey(TestAction2.NAME)); + + // get some specific actions and check the label + RecordsManagementAction cutoff = this.rmActionService.getDispositionAction("cutoff"); + assertNotNull(cutoff); + assertEquals("Cutoff", cutoff.getLabel()); + assertEquals("Cutoff", cutoff.getDescription()); + + RecordsManagementAction freeze = this.rmActionService.getRecordsManagementAction("freeze"); + assertNotNull(freeze); + assertEquals("Freeze", freeze.getLabel()); + assertEquals("Freeze", freeze.getLabel()); + + // test non-existent actions + assertNull(this.rmActionService.getDispositionAction("notThere")); + assertNull(this.rmActionService.getRecordsManagementAction("notThere")); + } + + public void testExecution() + { + RetryingTransactionCallback testCallback = new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + executionImpl(); + return null; + } + }; + txnHelper.doInTransaction(testCallback); + } + + public void beforeRMActionExecution(NodeRef nodeRef, String name, Map parameters) + { + if (inTest == true) + { + assertEquals(this.nodeRef, nodeRef); + assertEquals(TestAction.NAME, name); + assertEquals(1, parameters.size()); + assertTrue(parameters.containsKey(TestAction.PARAM)); + assertEquals(TestAction.PARAM_VALUE, parameters.get(TestAction.PARAM)); + beforeMarker = true; + } + } + + public void onRMActionExecution(NodeRef nodeRef, String name, Map parameters) + { + if (inTest == true) + { + assertEquals(this.nodeRef, nodeRef); + assertEquals(TestAction.NAME, name); + assertEquals(1, parameters.size()); + assertTrue(parameters.containsKey(TestAction.PARAM)); + assertEquals(TestAction.PARAM_VALUE, parameters.get(TestAction.PARAM)); + onMarker = true; + } + } + + private void executionImpl() + { + inTest = true; + try + { + policyComponent.bindClassBehaviour( + RecordsManagementPolicies.BEFORE_RM_ACTION_EXECUTION, + this, + new JavaBehaviour(this, "beforeRMActionExecution", NotificationFrequency.EVERY_EVENT)); + policyComponent.bindClassBehaviour( + RecordsManagementPolicies.ON_RM_ACTION_EXECUTION, + this, + new JavaBehaviour(this, "onRMActionExecution", NotificationFrequency.EVERY_EVENT)); + + assertFalse(beforeMarker); + assertFalse(onMarker); + assertFalse(this.nodeService.hasAspect(this.nodeRef, ASPECT_RECORD)); + + Map params = new HashMap(1); + params.put(TestAction.PARAM, TestAction.PARAM_VALUE); + this.rmActionService.executeRecordsManagementAction(this.nodeRef, TestAction.NAME, params); + + assertTrue(beforeMarker); + assertTrue(onMarker); + assertTrue(this.nodeService.hasAspect(this.nodeRef, ASPECT_RECORD)); + } + finally + { + inTest = false; + } + } + + public void testBulkExecution() + { + RetryingTransactionCallback testCallback = new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + bulkExecutionImpl(); + return null; + } + }; + txnHelper.doInTransaction(testCallback); + } + + private void bulkExecutionImpl() + { + for (NodeRef nodeRef : this.nodeRefs) + { + assertFalse(this.nodeService.hasAspect(nodeRef, ASPECT_RECORD)); + } + + Map params = new HashMap(1); + params.put(TestAction.PARAM, TestAction.PARAM_VALUE); + this.rmActionService.executeRecordsManagementAction(this.nodeRefs, TestAction.NAME, params); + + for (NodeRef nodeRef : this.nodeRefs) + { + assertTrue(this.nodeService.hasAspect(nodeRef, ASPECT_RECORD)); + } + } +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementAdminServiceImplTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementAdminServiceImplTest.java new file mode 100644 index 0000000000..7b07fc1c2a --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementAdminServiceImplTest.java @@ -0,0 +1,952 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.service; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.BeforeCreateReference; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.OnCreateReference; +import org.alfresco.module.org_alfresco_module_rm.caveat.RMListOfValuesConstraint; +import org.alfresco.module.org_alfresco_module_rm.caveat.RMListOfValuesConstraint.MatchLogic; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementCustomModel; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.script.CustomReferenceType; +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.Behaviour.NotificationFrequency; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.Constraint; +import org.alfresco.service.cmr.dictionary.ConstraintDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.util.Pair; +import org.springframework.util.CollectionUtils; + +/** + * This test class tests the definition and use of a custom RM elements at the Java services layer. + * + * @author Neil McErlean, janv, Roy Wetherall + */ +public class RecordsManagementAdminServiceImplTest extends BaseRMTestCase + implements RecordsManagementModel, + BeforeCreateReference, + OnCreateReference +{ + + private final static long testRunID = System.currentTimeMillis(); + + private List createdCustomProperties; + private List madeCustomisable; + + /** + * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase#setUp() + */ + @Override + protected void setUp() throws Exception + { + createdCustomProperties = new ArrayList(); + madeCustomisable = new ArrayList(); + super.setUp(); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase#setupTestData() + */ + @Override + protected void setupTestData() + { + super.setupTestData(); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase#tearDown() + */ + @Override + protected void tearDown() throws Exception + { + retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + // As system user + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + + for (QName createdCustomProperty : createdCustomProperties) + { + adminService.removeCustomPropertyDefinition(createdCustomProperty); + } + + for (QName customisable : madeCustomisable) + { + adminService.unmakeCustomisable(customisable); + } + + return null; + } + }); + + super.tearDown(); + } + + /** + * @see RecordsManagementAdminService#getCustomisable() + */ + public void testGetCustomisable() throws Exception + { + // Get the customisable types + doTestInTransaction(new Test() + { + @Override + public Void run() throws Exception + { + Set list = adminService.getCustomisable(); + assertNotNull(list); + assertTrue(list.containsAll( + CollectionUtils.arrayToList(new QName[] + { + ASPECT_RECORD, + TYPE_RECORD_FOLDER, + TYPE_NON_ELECTRONIC_DOCUMENT, + TYPE_RECORD_CATEGORY + }))); + + return null; + } + }); + } + + /** + * @see RecordsManagementAdminService#isCustomisable(QName) + */ + public void testIsCustomisable() throws Exception + { + doTestInTransaction(new Test() + { + @Override + public Void run() throws Exception + { + assertFalse(adminService.isCustomisable(TYPE_CONTENT)); + assertFalse(adminService.isCustomisable(ASPECT_DUBLINCORE)); + assertTrue(adminService.isCustomisable(TYPE_RECORD_FOLDER)); + assertTrue(adminService.isCustomisable(ASPECT_RECORD)); + + return null; + } + }); + } + + /** + * @see RecordsManagementAdminService#existsCustomProperty(QName) + * @see RecordsManagementAdminService#addCustomPropertyDefinition(QName, QName, String, QName, String, String, String, boolean, boolean, boolean, QName) + * @see RecordsManagementAdminService#addCustomPropertyDefinition(QName, QName, String, QName, String, String) + */ + public void testAddCustomPropertyDefinition() throws Exception + { + // Add property to Record (id specified, short version) + doTestInTransaction(new Test() + { + @Override + public QName run() throws Exception + { + // Check the property does not exist + assertFalse(adminService.existsCustomProperty(QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_URI, "myRecordProp1"))); + + return adminService.addCustomPropertyDefinition( + QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_URI, "myRecordProp1"), + ASPECT_RECORD, + "Label1", + DataTypeDefinition.TEXT, + "Title", + "Description"); + } + + @Override + public void test(QName result) throws Exception + { + try + { + // Check the property QName is correct + assertNotNull(result); + assertEquals(QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_URI, "myRecordProp1"), result); + assertTrue(adminService.existsCustomProperty(result)); + + // Check that property is available as a custom property + Map propDefs = adminService.getCustomPropertyDefinitions(ASPECT_RECORD); + assertNotNull(propDefs); + assertTrue(propDefs.containsKey(result)); + + // Check the property definition + PropertyDefinition propDef = propDefs.get(result); + assertNotNull(propDef); + assertEquals(DataTypeDefinition.TEXT, propDef.getDataType().getName()); + assertEquals("Description", propDef.getDescription()); + assertEquals("Label1", propDef.getTitle()); + } + finally + { + // Store the created property for cleanup later + createdCustomProperties.add(result); + } + } + }); + + // Add property to record (no id, short version) + doTestInTransaction(new Test() + { + @Override + public QName run() throws Exception + { + return adminService.addCustomPropertyDefinition( + null, + ASPECT_RECORD, + "Label2", + DataTypeDefinition.TEXT, + "Title", + "Description"); + } + + @Override + public void test(QName result) throws Exception + { + try + { + // Check the property QName is correct + assertNotNull(result); + assertEquals(RecordsManagementCustomModel.RM_CUSTOM_URI, result.getNamespaceURI()); + assertTrue(adminService.existsCustomProperty(result)); + + // Check that property is available as a custom property + Map propDefs = adminService.getCustomPropertyDefinitions(ASPECT_RECORD); + assertNotNull(propDefs); + assertTrue(propDefs.containsKey(result)); + + // Check the property definition + PropertyDefinition propDef = propDefs.get(result); + assertNotNull(propDef); + assertEquals(DataTypeDefinition.TEXT, propDef.getDataType().getName()); + assertEquals("Description", propDef.getDescription()); + assertEquals("Label2", propDef.getTitle()); + } + finally + { + // Store the created property for cleanup later + createdCustomProperties.add(result); + } + } + }); + + // Add property to record (long version) + doTestInTransaction(new Test() + { + @Override + public QName run() throws Exception + { + return adminService.addCustomPropertyDefinition( + null, + ASPECT_RECORD, + "Label3", + DataTypeDefinition.TEXT, + "Title", + "Description", + "default", + false, + false, + false, + null); + } + + @Override + public void test(QName result) throws Exception + { + try + { + // Check the property QName is correct + assertNotNull(result); + assertEquals(RecordsManagementCustomModel.RM_CUSTOM_URI, result.getNamespaceURI()); + assertTrue(adminService.existsCustomProperty(result)); + + // Check that property is available as a custom property + Map propDefs = adminService.getCustomPropertyDefinitions(ASPECT_RECORD); + assertNotNull(propDefs); + //assertEquals(3, propDefs.size()); + assertTrue(propDefs.containsKey(result)); + + // Check the property definition + PropertyDefinition propDef = propDefs.get(result); + assertNotNull(propDef); + assertEquals(DataTypeDefinition.TEXT, propDef.getDataType().getName()); + assertEquals("Description", propDef.getDescription()); + assertEquals("Label3", propDef.getTitle()); + assertEquals("default", propDef.getDefaultValue()); + assertFalse(propDef.isMandatory()); + assertFalse(propDef.isMultiValued()); + assertFalse(propDef.isProtected()); + + } + finally + { + // Store the created property for cleanup later + createdCustomProperties.add(result); + } + } + }); + + // Failure: Add a property with the same name twice + doTestInTransaction(new FailureTest + ( + "Can not create a property with the same id twice" + ) + { + @Override + public void run() + { + adminService.addCustomPropertyDefinition( + QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_URI, "myRecordProp1"), + ASPECT_RECORD, + "Label1", + DataTypeDefinition.TEXT, + "Title", + "Description"); + } + }); + + // Failure: Try and add a property to a type that isn't customisable + doTestInTransaction(new FailureTest + ( + "Can not add a custom property to a type that isn't registered as customisable" + ) + { + @Override + public void run() + { + adminService.addCustomPropertyDefinition( + QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_URI, "myContentProp"), + TYPE_CONTENT, + "Label1", + DataTypeDefinition.TEXT, + "Title", + "Description"); + } + }); + + // Failure: Add a property with the label twice (but no id specified) +// doTestInTransaction(new FailureTest +// ( +// "Can not create a property with the same label twice if no id is specified." +// ) +// { +// @Override +// public void run() +// { +// adminService.addCustomPropertyDefinition( +// null, +// ASPECT_RECORD, +// "Label1", +// DataTypeDefinition.TEXT, +// "Title", +// "Description"); +// } +// }); + } + + /** + * @see RecordsManagementAdminService#makeCustomisable(QName) + */ + public void testMakeCustomisable() throws Exception + { + doTestInTransaction(new Test() + { + @Override + public QName run() throws Exception + { + // Make a type customisable + assertFalse(adminService.isCustomisable(TYPE_CUSTOM_TYPE)); + adminService.makeCustomisable(TYPE_CUSTOM_TYPE); + madeCustomisable.add(TYPE_CUSTOM_TYPE); + assertTrue(adminService.isCustomisable(TYPE_CUSTOM_TYPE)); + + // Add a custom property + return adminService.addCustomPropertyDefinition( + QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_URI, "myNewProperty"), + TYPE_CUSTOM_TYPE, + "Label", + DataTypeDefinition.TEXT, + "Title", + "Description"); + } + + @Override + public void test(QName result) throws Exception + { + // Check the property QName is correct + assertNotNull(result); + assertEquals(QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_URI, "myNewProperty"), result); + assertTrue(adminService.existsCustomProperty(result)); + + // Check that property is available as a custom property + Map propDefs = adminService.getCustomPropertyDefinitions(TYPE_CUSTOM_TYPE); + assertNotNull(propDefs); + assertEquals(1, propDefs.size()); + assertTrue(propDefs.containsKey(result)); + + // Check the property definition + PropertyDefinition propDef = propDefs.get(result); + assertNotNull(propDef); + assertEquals(DataTypeDefinition.TEXT, propDef.getDataType().getName()); + assertEquals("Description", propDef.getDescription()); + assertEquals("Label", propDef.getTitle()); + + } + }); + + doTestInTransaction(new Test() + { + @Override + public QName run() throws Exception + { + // Make an aspect customisable + assertFalse(adminService.isCustomisable(ASPECT_CUSTOM_ASPECT)); + adminService.makeCustomisable(ASPECT_CUSTOM_ASPECT); + madeCustomisable.add(ASPECT_CUSTOM_ASPECT); + assertTrue(adminService.isCustomisable(ASPECT_CUSTOM_ASPECT)); + + // Add a custom property + return adminService.addCustomPropertyDefinition( + QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_URI, "myNewAspectProperty"), + ASPECT_CUSTOM_ASPECT, + "Label", + DataTypeDefinition.TEXT, + "Title", + "Description"); + } + + @Override + public void test(QName result) throws Exception + { + // Check the property QName is correct + assertNotNull(result); + assertEquals(QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_URI, "myNewAspectProperty"), result); + assertTrue(adminService.existsCustomProperty(result)); + + // Check that property is available as a custom property + Map propDefs = adminService.getCustomPropertyDefinitions(ASPECT_CUSTOM_ASPECT); + assertNotNull(propDefs); + assertEquals(1, propDefs.size()); + assertTrue(propDefs.containsKey(result)); + + // Check the property definition + PropertyDefinition propDef = propDefs.get(result); + assertNotNull(propDef); + assertEquals(DataTypeDefinition.TEXT, propDef.getDataType().getName()); + assertEquals("Description", propDef.getDescription()); + assertEquals("Label", propDef.getTitle()); + } + }); + } + + public void testUseCustomProperty() throws Exception + { + // Create custom property on type and aspect + doTestInTransaction(new Test() + { + @Override + public QName run() throws Exception + { + adminService.makeCustomisable(TYPE_CUSTOM_TYPE); + madeCustomisable.add(TYPE_CUSTOM_TYPE); + adminService.addCustomPropertyDefinition( + QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_URI, "myNewProperty"), + TYPE_CUSTOM_TYPE, + "Label", + DataTypeDefinition.TEXT, + "Title", + "Description"); + adminService.makeCustomisable(ASPECT_CUSTOM_ASPECT); + madeCustomisable.add(ASPECT_CUSTOM_ASPECT); + adminService.addCustomPropertyDefinition( + QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_URI, "myNewAspectProperty"), + ASPECT_CUSTOM_ASPECT, + "Label", + DataTypeDefinition.TEXT, + "Title", + "Description"); + + return null; + } + }); + + // Create nodes using custom type and aspect + doTestInTransaction(new Test() + { + @Override + public QName run() throws Exception + { + NodeRef customInstance1 = nodeService.createNode( + folder, + ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "myCustomInstance1"), + TYPE_CUSTOM_TYPE).getChildRef(); + NodeRef customInstance2 = nodeService.createNode( + folder, + ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "myCustomInstance2"), + TYPE_CONTENT).getChildRef(); + nodeService.addAspect(customInstance2, ASPECT_CUSTOM_ASPECT, null); + + // Assert that both instances have the custom aspects applied + assertTrue(nodeService.hasAspect(customInstance1, QName.createQName(RM_CUSTOM_URI, "rmtcustomTypeCustomProperties"))); + assertTrue(nodeService.hasAspect(customInstance2, QName.createQName(RM_CUSTOM_URI, "rmtcustomAspectCustomProperties"))); + + // Remove the custom aspect + nodeService.removeAspect(customInstance2, ASPECT_CUSTOM_ASPECT); + + // Assert the custom property aspect is no longer applied applied + assertTrue(nodeService.hasAspect(customInstance1, QName.createQName(RM_CUSTOM_URI, "rmtcustomTypeCustomProperties"))); + assertFalse(nodeService.hasAspect(customInstance2, QName.createQName(RM_CUSTOM_URI, "rmtcustomAspectCustomProperties"))); + + return null; + } + }); + } + + + public void testCreateAndUseCustomChildReference() throws Exception + { + long now = System.currentTimeMillis(); + createAndUseCustomReference(CustomReferenceType.PARENT_CHILD, null, "superseded" + now, "superseding" + now); + } + + public void testCreateAndUseCustomNonChildReference() throws Exception + { + long now = System.currentTimeMillis(); + createAndUseCustomReference(CustomReferenceType.BIDIRECTIONAL, "supporting" + now, null, null); + } + + private void createAndUseCustomReference(final CustomReferenceType refType, final String label, final String source, final String target) throws Exception + { + final NodeRef testRecord1 = retryingTransactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + NodeRef result = utils.createRecord(rmFolder, "testRecordA" + System.currentTimeMillis()); + return result; + } + }); + final NodeRef testRecord2 = retryingTransactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + NodeRef result = utils.createRecord(rmFolder, "testRecordB" + System.currentTimeMillis()); + return result; + } + }); + + final QName generatedQName = retryingTransactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public QName execute() throws Throwable + { + utils.declareRecord(testRecord1); + utils.declareRecord(testRecord2); + + Map params = new HashMap(); + params.put("referenceType", refType.toString()); + if (label != null) params.put("label", label); + if (source != null) params.put("source", source); + if (target != null) params.put("target", target); + + // Create the reference definition. + QName qNameResult; + if (label != null) + { + // A bidirectional reference + qNameResult = adminService.addCustomAssocDefinition(label); + } + else + { + // A parent/child reference + qNameResult = adminService.addCustomChildAssocDefinition(source, target); + } + System.out.println("Creating new " + refType + " reference definition: " + qNameResult); + System.out.println(" params- label: '" + label + "' source: '" + source + "' target: '" + target + "'"); + + return qNameResult; + } + }); + + retryingTransactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + // Confirm the custom reference is included in the list from adminService. + Map customRefDefinitions = adminService.getCustomReferenceDefinitions(); + AssociationDefinition retrievedRefDefn = customRefDefinitions.get(generatedQName); + assertNotNull("Custom reference definition from adminService was null.", retrievedRefDefn); + assertEquals(generatedQName, retrievedRefDefn.getName()); + assertEquals(refType.equals(CustomReferenceType.PARENT_CHILD), retrievedRefDefn.isChild()); + + // Now we need to use the custom reference. + // So we apply the aspect containing it to our test records. + nodeService.addAspect(testRecord1, ASPECT_CUSTOM_ASSOCIATIONS, null); + + QName assocsAspectQName = QName.createQName("rmc:customAssocs", namespaceService); + nodeService.addAspect(testRecord1, assocsAspectQName, null); + + if (CustomReferenceType.PARENT_CHILD.equals(refType)) + { + nodeService.addChild(testRecord1, testRecord2, generatedQName, generatedQName); + } + else + { + nodeService.createAssociation(testRecord1, testRecord2, generatedQName); + } + return null; + } + }); + + retryingTransactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + // Read back the reference value to make sure it was correctly applied. + List childAssocs = nodeService.getChildAssocs(testRecord1); + List retrievedAssocs = nodeService.getTargetAssocs(testRecord1, RegexQNamePattern.MATCH_ALL); + + Object newlyAddedRef = null; + if (CustomReferenceType.PARENT_CHILD.equals(refType)) + { + for (ChildAssociationRef caRef : childAssocs) + { + QName refInstanceQName = caRef.getQName(); + if (generatedQName.equals(refInstanceQName)) newlyAddedRef = caRef; + } + } + else + { + for (AssociationRef aRef : retrievedAssocs) + { + QName refQName = aRef.getTypeQName(); + if (generatedQName.equals(refQName)) newlyAddedRef = aRef; + } + } + assertNotNull("newlyAddedRef was null.", newlyAddedRef); + + // Check that the reference has appeared in the data dictionary + AspectDefinition customAssocsAspect = dictionaryService.getAspect(ASPECT_CUSTOM_ASSOCIATIONS); + assertNotNull(customAssocsAspect); + if (CustomReferenceType.PARENT_CHILD.equals(refType)) + { + assertNotNull("The customReference is not returned from the dictionaryService.", + customAssocsAspect.getChildAssociations().get(generatedQName)); + } + else + { + assertNotNull("The customReference is not returned from the dictionaryService.", + customAssocsAspect.getAssociations().get(generatedQName)); + } + return null; + } + }); + } + + public void testGetAllProperties() + { + retryingTransactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + // Just dump them out for visual inspection + System.out.println("Available custom properties:"); + Map props = adminService.getCustomPropertyDefinitions(); + for (QName prop : props.keySet()) + { + System.out.println(" - " + prop.toString()); + + String propId = props.get(prop).getTitle(); + assertNotNull("null client-id for " + prop, propId); + + System.out.println(" " + propId); + } + return null; + } + }); + } + + public void testGetAllReferences() + { + retryingTransactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + // Just dump them out for visual inspection + System.out.println("Available custom references:"); + Map references = adminService.getCustomReferenceDefinitions(); + for (QName reference : references.keySet()) + { + System.out.println(" - " + reference.toString()); + System.out.println(" " + references.get(reference).getTitle()); + } + return null; + } + }); + } + + public void testGetAllConstraints() + { + retryingTransactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + // Just dump them out for visual inspection + System.out.println("Available custom constraints:"); + List constraints = adminService.getCustomConstraintDefinitions(RecordsManagementCustomModel.RM_CUSTOM_MODEL); + for (ConstraintDefinition constraint : constraints) + { + System.out.println(" - " + constraint.getName()); + System.out.println(" " + constraint.getTitle()); + } + return null; + } + }); + } + + private boolean beforeMarker = false; + private boolean onMarker = false; + @SuppressWarnings("unused") + private boolean inTest = false; + + public void testCreateReference() throws Exception + { + inTest = true; + try + { + // Create the necessary test objects in the db: two records. + final Pair testRecords = retryingTransactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback>() + { + public Pair execute() throws Throwable + { + NodeRef rec1 = utils.createRecord(rmFolder, "testRecordA" + System.currentTimeMillis()); + NodeRef rec2 = utils.createRecord(rmFolder, "testRecordB" + System.currentTimeMillis()); + Pair result = new Pair(rec1, rec2); + return result; + } + }); + final NodeRef testRecord1 = testRecords.getFirst(); + final NodeRef testRecord2 = testRecords.getSecond(); + + retryingTransactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + utils.declareRecord(testRecord1); + utils.declareRecord(testRecord2); + + policyComponent.bindClassBehaviour( + RecordsManagementPolicies.BEFORE_CREATE_REFERENCE, + this, + new JavaBehaviour(RecordsManagementAdminServiceImplTest.this, "beforeCreateReference", NotificationFrequency.EVERY_EVENT)); + policyComponent.bindClassBehaviour( + RecordsManagementPolicies.ON_CREATE_REFERENCE, + this, + new JavaBehaviour(RecordsManagementAdminServiceImplTest.this, "onCreateReference", NotificationFrequency.EVERY_EVENT)); + + assertFalse(beforeMarker); + assertFalse(onMarker); + + adminService.addCustomReference(testRecord1, testRecord2, CUSTOM_REF_VERSIONS); + + assertTrue(beforeMarker); + assertTrue(onMarker); + return null; + } + }); + } + finally + { + inTest = false; + } + } + + public void beforeCreateReference(NodeRef fromNodeRef, NodeRef toNodeRef, QName reference) + { + beforeMarker = true; + } + + public void onCreateReference(NodeRef fromNodeRef, NodeRef toNodeRef, QName reference) + { + onMarker = true; + } + + public void testCreateCustomConstraints() throws Exception + { + final int beforeCnt = + retryingTransactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Integer execute() throws Throwable + { + List result = adminService.getCustomConstraintDefinitions(RecordsManagementCustomModel.RM_CUSTOM_MODEL); + assertNotNull(result); + return result.size(); + } + }); + + final String conTitle = "test title - "+testRunID; + final List allowedValues = new ArrayList(3); + allowedValues.add("RED"); + allowedValues.add("AMBER"); + allowedValues.add("GREEN"); + + final QName testCon = retryingTransactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public QName execute() throws Throwable + { + String conLocalName = "test-"+testRunID; + + final QName result = QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_URI, conLocalName); + + adminService.addCustomConstraintDefinition(result, conTitle, true, allowedValues, MatchLogic.AND); + return result; + } + }); + + + // Set the current security context as System - to see allowed values (unless caveat config is also updated for admin) + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + + retryingTransactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + List customConstraintDefs = adminService.getCustomConstraintDefinitions(RecordsManagementCustomModel.RM_CUSTOM_MODEL); + assertEquals(beforeCnt+1, customConstraintDefs.size()); + + boolean found = false; + for (ConstraintDefinition conDef : customConstraintDefs) + { + if (conDef.getName().equals(testCon)) + { + assertEquals(conTitle, conDef.getTitle()); + + Constraint con = conDef.getConstraint(); + assertTrue(con instanceof RMListOfValuesConstraint); + + assertEquals("LIST", ((RMListOfValuesConstraint)con).getType()); + assertEquals(3, ((RMListOfValuesConstraint)con).getAllowedValues().size()); + + found = true; + break; + } + } + assertTrue(found); + return null; + } + }); + + + // Set the current security context as admin + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + + retryingTransactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + allowedValues.clear(); + allowedValues.add("RED"); + allowedValues.add("YELLOW"); + + adminService.changeCustomConstraintValues(testCon, allowedValues); + return null; + } + }); + + // Set the current security context as System - to see allowed values (unless caveat config is also updated for admin) + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + + retryingTransactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + List customConstraintDefs = adminService.getCustomConstraintDefinitions(RecordsManagementCustomModel.RM_CUSTOM_MODEL); + assertEquals(beforeCnt+1, customConstraintDefs.size()); + + boolean found = false; + for (ConstraintDefinition conDef : customConstraintDefs) + { + if (conDef.getName().equals(testCon)) + { + assertEquals(conTitle, conDef.getTitle()); + + Constraint con = conDef.getConstraint(); + assertTrue(con instanceof RMListOfValuesConstraint); + + assertEquals("LIST", ((RMListOfValuesConstraint)con).getType()); + assertEquals(2, ((RMListOfValuesConstraint)con).getAllowedValues().size()); + + found = true; + break; + } + } + assertTrue(found); + return null; + } + }); + + + // Set the current security context as admin + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + + // Add custom property to record with test constraint + retryingTransactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + String propLocalName = "myProp-"+testRunID; + + QName dataType = DataTypeDefinition.TEXT; + String propTitle = "My property title"; + String description = "My property description"; + String defaultValue = null; + boolean multiValued = false; + boolean mandatory = false; + boolean isProtected = false; + + QName propName = adminService.addCustomPropertyDefinition(null, ASPECT_RECORD, propLocalName, dataType, propTitle, description, defaultValue, multiValued, mandatory, isProtected, testCon); + createdCustomProperties.add(propName); + return null; + } + }); + } +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementAuditServiceImplTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementAuditServiceImplTest.java new file mode 100644 index 0000000000..3d2ec5cd27 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementAuditServiceImplTest.java @@ -0,0 +1,427 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.service; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import junit.framework.TestCase; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditEntry; +import org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditQueryParameters; +import org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditService; +import org.alfresco.module.org_alfresco_module_rm.test.util.TestUtilities; +import org.alfresco.repo.security.authentication.AuthenticationException; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.ApplicationContextHelper; +import org.alfresco.util.EqualsHelper; +import org.springframework.context.ApplicationContext; + +/** + * @see RecordsManagementAuditService + * + * @author Derek Hulley + * @since 3.2 + */ +public class RecordsManagementAuditServiceImplTest extends TestCase +{ + private ApplicationContext ctx; + + private ServiceRegistry serviceRegistry; + private NodeService nodeService; + private TransactionService transactionService; + private RetryingTransactionHelper txnHelper; + private RecordsManagementAuditService rmAuditService; + + + private Date testStartTime; + private NodeRef filePlan; + + @Override + protected void setUp() throws Exception + { + testStartTime = new Date(); + + // We require that records management auditing is enabled + // This gets done by the AMP, but as we're not running from + // and AMP, we need to do it ourselves! + System.setProperty("audit.rm.enabled", "true"); + + // Now we can fetch the context + ctx = ApplicationContextHelper.getApplicationContext(); + + this.serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY); + this.transactionService = serviceRegistry.getTransactionService(); + this.txnHelper = transactionService.getRetryingTransactionHelper(); + + this.rmAuditService = (RecordsManagementAuditService) ctx.getBean("RecordsManagementAuditService"); + + this.nodeService = serviceRegistry.getNodeService(); + + + // Set the current security context as admin + AuthenticationUtil.setRunAsUser(AuthenticationUtil.getSystemUserName()); + + // Stop and clear the log + rmAuditService.stop(); + rmAuditService.clear(); + rmAuditService.start(); + + RetryingTransactionCallback setUpCallback = new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + if (filePlan == null) + { + filePlan = TestUtilities.loadFilePlanData(ctx); + } + updateFilePlan(); + return null; + } + }; + txnHelper.doInTransaction(setUpCallback); + } + + @Override + protected void tearDown() + { + AuthenticationUtil.clearCurrentSecurityContext(); + try + { + rmAuditService.start(); + } + catch (Throwable e) + { + // Not too important + } + } + + /** + * Perform a full query audit for RM + * @return Returns all the results + */ + private List queryAll() + { + RetryingTransactionCallback> testCallback = + new RetryingTransactionCallback>() + { + public List execute() throws Throwable + { + RecordsManagementAuditQueryParameters params = new RecordsManagementAuditQueryParameters(); + List entries = rmAuditService.getAuditTrail(params); + return entries; + } + }; + return txnHelper.doInTransaction(testCallback); + } + + /** + * Create a new fileplan + */ + private void updateFilePlan() + { + RetryingTransactionCallback updateCallback = new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + // Do some stuff + nodeService.setProperty(filePlan, ContentModel.PROP_TITLE, "File Plan - " + System.currentTimeMillis()); + + return null; + } + }; + txnHelper.doInTransaction(updateCallback); + } + + public void testSetUp() + { + // Just to get get the fileplan set up + } + + public void testQuery_All() + { + queryAll(); + } + + public void testQuery_UserLimited() + { + // Make sure that something has been done + updateFilePlan(); + + final int limit = 1; + final String user = AuthenticationUtil.getSystemUserName(); // The user being tested + + RetryingTransactionCallback> testCallback = + new RetryingTransactionCallback>() + { + public List execute() throws Throwable + { + RecordsManagementAuditQueryParameters params = new RecordsManagementAuditQueryParameters(); + params.setUser(user); + params.setMaxEntries(limit); + List entries = rmAuditService.getAuditTrail(params); + return entries; + } + }; + List entries = txnHelper.doInTransaction(testCallback); + assertNotNull(entries); + assertEquals("Expected results to be limited", limit, entries.size()); + } + + public void testQuery_Node() throws InterruptedException + { + RetryingTransactionCallback> allResultsCallback = + new RetryingTransactionCallback>() + { + public List execute() throws Throwable + { + RecordsManagementAuditQueryParameters params = new RecordsManagementAuditQueryParameters(); + params.setDateFrom(testStartTime); + List entries = rmAuditService.getAuditTrail(params); + return entries; + } + }; + List entries = txnHelper.doInTransaction(allResultsCallback); + assertNotNull("Expect a list of results for the query", entries); + + // Find all results for a given node + NodeRef chosenNodeRef = null; + int count = 0; + for (RecordsManagementAuditEntry entry : entries) + { + NodeRef nodeRef = entry.getNodeRef(); + assertNotNull("Found entry with null nodeRef: " + entry, nodeRef); + if (chosenNodeRef == null) + { + chosenNodeRef = nodeRef; + count++; + } + else if (nodeRef.equals(chosenNodeRef)) + { + count++; + } + } + + final NodeRef chosenNodeRefFinal = chosenNodeRef; + // Now search again, but for the chosen node + RetryingTransactionCallback> nodeResultsCallback = + new RetryingTransactionCallback>() + { + public List execute() throws Throwable + { + RecordsManagementAuditQueryParameters params = new RecordsManagementAuditQueryParameters(); + params.setDateFrom(testStartTime); + params.setNodeRef(chosenNodeRefFinal); + List entries = rmAuditService.getAuditTrail(params); + return entries; + } + }; + entries = txnHelper.doInTransaction(nodeResultsCallback); + assertNotNull("Expect a list of results for the query", entries); + assertTrue("No results were found for node: " + chosenNodeRefFinal, entries.size() > 0); + // We can't check the size because we need entries for the node and any children as well + + Thread.sleep(5000); + + // Clear the log + rmAuditService.clear(); + + entries = txnHelper.doInTransaction(nodeResultsCallback); + assertTrue("Should have cleared all audit entries", entries.isEmpty()); + + // Delete the node + txnHelper.doInTransaction(new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + return AuthenticationUtil.runAs(new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + nodeService.deleteNode(chosenNodeRefFinal); + return null; + } + }, AuthenticationUtil.getSystemUserName()); + } + }); + + Thread.sleep(5000); + + entries = txnHelper.doInTransaction(nodeResultsCallback); + assertFalse("Should have recorded node deletion", entries.isEmpty()); + } + + public void testStartStopDelete() throws InterruptedException + { + // Stop the audit + rmAuditService.stop(); + + Thread.sleep(5000); + + List result1 = queryAll(); + assertNotNull(result1); + + // Update the fileplan + updateFilePlan(); + + Thread.sleep(5000); + + // There should be no new audit entries + List result2 = queryAll(); + assertNotNull(result2); + assertEquals( + "Audit results should not have changed after auditing was disabled", + result1.size(), result2.size()); + + // repeat with a start + rmAuditService.start(); + updateFilePlan(); + + Thread.sleep(5000); + + List result3 = queryAll(); + assertNotNull(result3); + assertTrue( + "Expected more results after enabling audit", + result3.size() > result1.size()); + + Thread.sleep(5000); + + // Stop and delete all entries + rmAuditService.stop(); + rmAuditService.clear(); + + // There should be no entries + List result4 = queryAll(); + assertNotNull(result4); + assertEquals( + "Audit entries should have been cleared", + 0, result4.size()); + } + + public void xtestAuditAuthentication() + { + rmAuditService.stop(); + rmAuditService.clear(); + rmAuditService.start(); + + MutableAuthenticationService authenticationService = serviceRegistry.getAuthenticationService(); + PersonService personService = serviceRegistry.getPersonService(); + + try + { + personService.deletePerson("baboon"); + authenticationService.deleteAuthentication("baboon"); + } + catch (Throwable e) + { + // Not serious + } + + // Failed login attempt ... + try + { + AuthenticationUtil.pushAuthentication(); + authenticationService.authenticate("baboon", "lskdfj".toCharArray()); + fail("Expected authentication failure"); + } + catch (AuthenticationException e) + { + // Good + } + finally + { + AuthenticationUtil.popAuthentication(); + } + rmAuditService.stop(); + List result1 = queryAll(); + // Check that the username is reflected correctly in the results + assertFalse("No audit results were generated for the failed login.", result1.isEmpty()); + boolean found = false; + for (RecordsManagementAuditEntry entry : result1) + { + String userName = entry.getUserName(); + if (userName.equals("baboon")) + { + found = true; + break; + } + } + assertTrue("Expected to hit failed login attempt for user", found); + + // Test successful authentication + try + { + personService.deletePerson("cdickons"); + authenticationService.deleteAuthentication("cdickons"); + } + catch (Throwable e) + { + // Not serious + } + authenticationService.createAuthentication("cdickons", getName().toCharArray()); + Map personProperties = new HashMap(); + personProperties.put(ContentModel.PROP_USERNAME, "cdickons"); + personProperties.put(ContentModel.PROP_FIRSTNAME, "Charles"); + personProperties.put(ContentModel.PROP_LASTNAME, "Dickons"); + personService.createPerson(personProperties); + + rmAuditService.clear(); + rmAuditService.start(); + try + { + AuthenticationUtil.pushAuthentication(); + authenticationService.authenticate("cdickons", getName().toCharArray()); + } + finally + { + AuthenticationUtil.popAuthentication(); + } + rmAuditService.stop(); + List result2 = queryAll(); + found = false; + for (RecordsManagementAuditEntry entry : result2) + { + String userName = entry.getUserName(); + String fullName = entry.getFullName(); + if (userName.equals("cdickons") && EqualsHelper.nullSafeEquals(fullName, "Charles Dickons")) + { + found = true; + break; + } + } + assertTrue("Expected to hit successful login attempt for Charles Dickons (cdickons)", found); + } +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementEventServiceImplTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementEventServiceImplTest.java new file mode 100644 index 0000000000..1bfecc00c8 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementEventServiceImplTest.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.service; + +import java.util.List; + +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEvent; +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService; +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventType; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.util.BaseSpringTest; + +/** + * Event service implementation unit test + * + * @author Roy Wetherall + */ +public class RecordsManagementEventServiceImplTest extends BaseSpringTest implements RecordsManagementModel +{ + protected static StoreRef SPACES_STORE = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore"); + + private RecordsManagementEventService rmEventService; + private RetryingTransactionHelper transactionHelper; + + @Override + protected void onSetUpInTransaction() throws Exception + { + super.onSetUpInTransaction(); + + // Get the service required in the tests + this.rmEventService = (RecordsManagementEventService)this.applicationContext.getBean("RecordsManagementEventService"); + this.transactionHelper = (RetryingTransactionHelper)this.applicationContext.getBean("retryingTransactionHelper"); + + // Set the current security context as admin + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + } + + public void testGetEventTypes() + { + setComplete(); + endTransaction(); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + List eventTypes = rmEventService.getEventTypes(); + assertNotNull(eventTypes); + for (RecordsManagementEventType eventType : eventTypes) + { + System.out.println(eventType.getName() + " - " + eventType.getDisplayLabel()); + } + return null; + } + }); + } + + public void testGetEvents() + { + setComplete(); + endTransaction(); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + List events = rmEventService.getEvents(); + assertNotNull(events); + for (RecordsManagementEvent event : events) + { + System.out.println(event.getName()); + } + return null; + } + }); + } + + public void testAddRemoveEvents() + { + setComplete(); + endTransaction(); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + List events = rmEventService.getEvents(); + assertNotNull(events); + assertFalse(containsEvent(events, "myEvent")); + + rmEventService.addEvent("rmEventType.simple", "myEvent", "My Event"); + + events = rmEventService.getEvents(); + assertNotNull(events); + assertTrue(containsEvent(events, "myEvent")); + + rmEventService.removeEvent("myEvent"); + + events = rmEventService.getEvents(); + assertNotNull(events); + assertFalse(containsEvent(events, "myEvent")); + return null; + } + }); + } + + private boolean containsEvent(List events, String eventName) + { + boolean result = false; + for (RecordsManagementEvent event : events) + { + if (eventName.equals(event.getName()) == true) + { + result = true; + break; + } + } + return result; + } +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementSearchServiceImplTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementSearchServiceImplTest.java new file mode 100644 index 0000000000..085ef35879 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementSearchServiceImplTest.java @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.service; + +import java.util.List; + +import org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchParameters; +import org.alfresco.module.org_alfresco_module_rm.search.SavedSearchDetails; +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.util.TestWithUserUtils; + +/** + * Search service implementation unit test. + * + * @author Roy Wetherall + */ +public class RecordsManagementSearchServiceImplTest extends BaseRMTestCase +{ + @Override + protected boolean isMultiHierarchyTest() + { + return true; + } + + private static final String SEARCH1 = "search1"; + private static final String SEARCH2 = "search2"; + private static final String SEARCH3 = "search3"; + private static final String SEARCH4 = "search4"; + + private static final String USER1 = "user1"; + private static final String USER2 = "user2"; + + private NodeRef folderLevelRecordFolder; + private NodeRef recordLevelRecordFolder; + + private NodeRef recordOne; + private NodeRef recordTwo; + private NodeRef recordThree; + private NodeRef recordFour; + private NodeRef recordFive; + private NodeRef recordSix; + + private MutableAuthenticationService authenticationService; + + private int numberOfReports; + + /** + * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase#setupTestData() + */ + @Override + protected void setupTestData() + { + super.setupTestData(); + + authenticationService = (MutableAuthenticationService)applicationContext.getBean("AuthenticationService"); + + doTestInTransaction(new Test() + { + @Override + public Void run() + { + // Create test users + TestWithUserUtils.createUser(USER1, USER1, rootNodeRef, nodeService, authenticationService); + TestWithUserUtils.createUser(USER2, USER2, rootNodeRef, nodeService, authenticationService); + + // Count the number of pre-defined reports + List searches = rmSearchService.getSavedSearches(SITE_ID); + assertNotNull(searches); + numberOfReports = searches.size(); + + return null; + } + }); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase#setupMultiHierarchyTestData() + */ + @Override + protected void setupMultiHierarchyTestData() + { + super.setupMultiHierarchyTestData(); + + doTestInTransaction(new Test() + { + @Override + public Void run() + { + folderLevelRecordFolder = mhRecordFolder42; + recordLevelRecordFolder = mhRecordFolder43; + + recordOne = utils.createRecord(folderLevelRecordFolder, "recordOne.txt", null, "record one - folder level - elephant"); + recordTwo = utils.createRecord(folderLevelRecordFolder, "recordTwo.txt", null, "record two - folder level - snake"); + recordThree = utils.createRecord(folderLevelRecordFolder, "recordThree.txt", null, "record three - folder level - monkey"); + recordFour = utils.createRecord(recordLevelRecordFolder, "recordFour.txt", null, "record four - record level - elephant"); + recordFive = utils.createRecord(recordLevelRecordFolder, "recordFive.txt", null, "record five - record level - snake"); + recordSix = utils.createRecord(recordLevelRecordFolder, "recordSix.txt", null, "record six - record level - monkey"); + + return null; + } + }); + } + + @Override + protected void tearDown() throws Exception + { + super.tearDown(); + + doTestInTransaction(new Test() + { + @Override + public Void run() + { + // Delete test users + TestWithUserUtils.deleteUser(USER1, USER1, rootNodeRef, nodeService, authenticationService); + TestWithUserUtils.deleteUser(USER2, USER2, rootNodeRef, nodeService, authenticationService); + + return null; + } + }); + } + + public void testSearch() + { + // Full text search + doTestInTransaction(new Test() + { + @Override + public Void run() + { + String query = "keywords:\"elephant\""; + RecordsManagementSearchParameters params = new RecordsManagementSearchParameters(); + params.setIncludeUndeclaredRecords(true); + List results = rmSearchService.search(SITE_ID, query, params); + assertNotNull(results); + assertEquals(2, results.size()); + + return null; + } + }); + + // Property search + + // + } + + public void testSaveSearch() + { + // Add some saved searches (as admin user) + doTestInTransaction(new Test() + { + @Override + public Void run() + { + SavedSearchDetails details1 = rmSearchService.saveSearch(SITE_ID, SEARCH1, "description1", "query1", new RecordsManagementSearchParameters(), true); + checkSearchDetails(details1, "mySite", "search1", "description1", "query1", new RecordsManagementSearchParameters(), true); + SavedSearchDetails details2 = rmSearchService.saveSearch(SITE_ID, SEARCH2, "description2", "query2", new RecordsManagementSearchParameters(), false); + checkSearchDetails(details2, "mySite", "search2", "description2", "query2", new RecordsManagementSearchParameters(), false); + + return null; + } + + }); + + // Add some saved searches (as user1) + doTestInTransaction(new Test() + { + @Override + public Void run() + { + SavedSearchDetails details1 = rmSearchService.saveSearch(SITE_ID, SEARCH3, "description3", "query3", new RecordsManagementSearchParameters(), false); + checkSearchDetails(details1, "mySite", SEARCH3, "description3", "query3", new RecordsManagementSearchParameters(), false); + SavedSearchDetails details2 = rmSearchService.saveSearch(SITE_ID, SEARCH4, "description4", "query4", new RecordsManagementSearchParameters(), false); + checkSearchDetails(details2, "mySite", SEARCH4, "description4", "query4", new RecordsManagementSearchParameters(), false); + + return null; + } + + }, USER1); + + // Get searches (as admin user) + doTestInTransaction(new Test() + { + @Override + public Void run() + { + List searches = rmSearchService.getSavedSearches(SITE_ID); + assertNotNull(searches); + assertEquals(numberOfReports + 2, searches.size()); + + SavedSearchDetails search1 = rmSearchService.getSavedSearch(SITE_ID, SEARCH1); + assertNotNull(search1); + checkSearchDetails(search1, "mySite", "search1", "description1", "query1", new RecordsManagementSearchParameters(), true); + + SavedSearchDetails search2 = rmSearchService.getSavedSearch(SITE_ID, SEARCH2); + assertNotNull(search2); + checkSearchDetails(search2, "mySite", "search2", "description2", "query2", new RecordsManagementSearchParameters(), false); + + SavedSearchDetails search3 = rmSearchService.getSavedSearch(SITE_ID, SEARCH3); + assertNull(search3); + + SavedSearchDetails search4 = rmSearchService.getSavedSearch(SITE_ID, SEARCH4); + assertNull(search4); + + return null; + } + + }); + + // Get searches (as user1) + doTestInTransaction(new Test() + { + @Override + public Void run() + { + List searches = rmSearchService.getSavedSearches(SITE_ID); + assertNotNull(searches); + assertEquals(numberOfReports + 3, searches.size()); + + SavedSearchDetails search1 = rmSearchService.getSavedSearch(SITE_ID, SEARCH1); + assertNotNull(search1); + checkSearchDetails(search1, "mySite", "search1", "description1", "query1", new RecordsManagementSearchParameters(), true); + + SavedSearchDetails search2 = rmSearchService.getSavedSearch(SITE_ID, SEARCH2); + assertNull(search2); + + SavedSearchDetails search3 = rmSearchService.getSavedSearch(SITE_ID, SEARCH3); + assertNotNull(search3); + checkSearchDetails(search3, "mySite", SEARCH3, "description3", "query3", new RecordsManagementSearchParameters(), false); + + SavedSearchDetails search4 = rmSearchService.getSavedSearch(SITE_ID, SEARCH4); + assertNotNull(search4); + checkSearchDetails(search4, "mySite", "search4", "description4", "query4", new RecordsManagementSearchParameters(), false); + + return null; + } + + }, USER1); + + // Update search (as admin user) + doTestInTransaction(new Test() + { + @Override + public Void run() + { + SavedSearchDetails search1 = rmSearchService.getSavedSearch(SITE_ID, SEARCH1); + assertNotNull(search1); + checkSearchDetails(search1, SITE_ID, SEARCH1, "description1", "query1", new RecordsManagementSearchParameters(), true); + + rmSearchService.saveSearch(SITE_ID, SEARCH1, "change", "change", new RecordsManagementSearchParameters(), true); + + search1 = rmSearchService.getSavedSearch(SITE_ID, SEARCH1); + assertNotNull(search1); + checkSearchDetails(search1, SITE_ID, SEARCH1, "change", "change", new RecordsManagementSearchParameters(), true); + + return null; + } + }); + + // Delete searches (as admin user) + // TODO + } + + /** + * Check the details of the saved search. + */ + private void checkSearchDetails( + SavedSearchDetails details, + String siteid, + String name, + String description, + String query, + RecordsManagementSearchParameters searchParameters, + boolean isPublic) + { + assertNotNull(details); + assertEquals(siteid, details.getSiteId()); + assertEquals(name, details.getName()); + assertEquals(description, details.getDescription()); + assertEquals(query, details.getSearch()); + assertEquals(isPublic, details.isPublic()); + + assertEquals(searchParameters.getMaxItems(), details.getSearchParameters().getMaxItems()); + assertEquals(searchParameters.isIncludeRecords(), details.getSearchParameters().isIncludeRecords()); + assertEquals(searchParameters.isIncludeUndeclaredRecords(), details.getSearchParameters().isIncludeUndeclaredRecords()); + assertEquals(searchParameters.isIncludeVitalRecords(), details.getSearchParameters().isIncludeVitalRecords()); + assertEquals(searchParameters.isIncludeRecordFolders(), details.getSearchParameters().isIncludeRecordFolders()); + assertEquals(searchParameters.isIncludeFrozen(), details.getSearchParameters().isIncludeFrozen()); + assertEquals(searchParameters.isIncludeCutoff(), details.getSearchParameters().isIncludeCutoff()); + + // Check the other stuff .... + } +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementSecurityServiceImplTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementSecurityServiceImplTest.java new file mode 100644 index 0000000000..cc414805c0 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementSecurityServiceImplTest.java @@ -0,0 +1,692 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.service; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; +import org.alfresco.module.org_alfresco_module_rm.capability.Capability; +import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService; +import org.alfresco.module.org_alfresco_module_rm.security.Role; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.security.AccessPermission; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.BaseSpringTest; +import org.alfresco.util.GUID; +import org.alfresco.util.PropertyMap; + +/** + * Event service implementation unit test + * + * @author Roy Wetherall + */ +public class RecordsManagementSecurityServiceImplTest extends BaseSpringTest + implements RecordsManagementModel +{ + protected static StoreRef SPACES_STORE = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore"); + + private NodeService nodeService; + private MutableAuthenticationService authenticationService; + private AuthorityService authorityService; + private PermissionService permissionService; + private PersonService personService; + private RecordsManagementSecurityService rmSecurityService; + private RecordsManagementActionService rmActionService; + private RetryingTransactionHelper transactionHelper; + + @Override + protected void onSetUpInTransaction() throws Exception + { + super.onSetUpInTransaction(); + + // Get the service required in the tests + this.nodeService = (NodeService)this.applicationContext.getBean("NodeService"); + this.authenticationService = (MutableAuthenticationService)this.applicationContext.getBean("AuthenticationService"); + this.personService = (PersonService)this.applicationContext.getBean("PersonService"); + this.authorityService = (AuthorityService)this.applicationContext.getBean("authorityService"); + this.rmSecurityService = (RecordsManagementSecurityService)this.applicationContext.getBean("RecordsManagementSecurityService"); + this.transactionHelper = (RetryingTransactionHelper)this.applicationContext.getBean("retryingTransactionHelper"); + this.permissionService = (PermissionService)this.applicationContext.getBean("PermissionService"); + this.rmActionService = (RecordsManagementActionService)this.applicationContext.getBean("RecordsManagementActionService"); + + // Set the current security context as admin + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + } + + public void testRoles() + { + final NodeRef rmRootNode = createRMRootNodeRef(); + + setComplete(); + endTransaction(); + + transactionHelper.doInTransaction(new RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + Set roles = rmSecurityService.getRoles(rmRootNode); + assertNotNull(roles); + assertEquals(5, roles.size()); + + rmSecurityService.createRole(rmRootNode, "MyRole", "My Role", getListOfCapabilities(5)); + + roles = rmSecurityService.getRoles(rmRootNode); + assertNotNull(roles); + assertEquals(6, roles.size()); + + Role role = findRole(roles, "MyRole"); + assertNotNull(role); + assertEquals("MyRole", role.getName()); + assertEquals("My Role", role.getDisplayLabel()); + assertNotNull(role.getCapabilities()); + assertEquals(5, role.getCapabilities().size()); + assertNotNull(role.getRoleGroupName()); + + // Add a user to the role + String userName = createAndAddUserToRole(role.getRoleGroupName()); + + // Check that we can retrieve the users roles + Set userRoles = rmSecurityService.getRolesByUser(rmRootNode, userName); + assertNotNull(userRoles); + assertEquals(1, userRoles.size()); + Role userRole = userRoles.iterator().next(); + assertEquals("MyRole", userRole.getName()); + + try + { + rmSecurityService.createRole(rmRootNode, "MyRole", "My Role", getListOfCapabilities(5)); + fail("Duplicate role id's not allowed for the same rm root node"); + } + catch (AlfrescoRuntimeException e) + { + // Expected + } + + rmSecurityService.createRole(rmRootNode, "MyRole2", "My Role", getListOfCapabilities(5)); + + roles = rmSecurityService.getRoles(rmRootNode); + assertNotNull(roles); + assertEquals(7, roles.size()); + + Set list = getListOfCapabilities(3, 4); + assertEquals(3, list.size()); + + Role result = rmSecurityService.updateRole(rmRootNode, "MyRole", "SomethingDifferent", list); + + assertNotNull(result); + assertEquals("MyRole", result.getName()); + assertEquals("SomethingDifferent", result.getDisplayLabel()); + assertNotNull(result.getCapabilities()); + assertEquals(3, result.getCapabilities().size()); + assertNotNull(result.getRoleGroupName()); + + roles = rmSecurityService.getRoles(rmRootNode); + assertNotNull(roles); + assertEquals(7, roles.size()); + + Role role2 = findRole(roles, "MyRole"); + assertNotNull(role2); + assertEquals("MyRole", role2.getName()); + assertEquals("SomethingDifferent", role2.getDisplayLabel()); + assertNotNull(role2.getCapabilities()); + assertEquals(3, role2.getCapabilities().size()); + assertNotNull(role2.getRoleGroupName()); + + rmSecurityService.deleteRole(rmRootNode, "MyRole2"); + + roles = rmSecurityService.getRoles(rmRootNode); + assertNotNull(roles); + assertEquals(6, roles.size()); + + return null; + } + }); + } + + private Role findRole(Set roles, String name) + { + Role result = null; + for (Role role : roles) + { + if (name.equals(role.getName()) == true) + { + result = role; + break; + } + } + + return result; + } + + private Set getListOfCapabilities(int size) + { + return getListOfCapabilities(size, 0); + } + + private Set getListOfCapabilities(int size, int offset) + { + Set result = new HashSet(size); + Set caps = rmSecurityService.getCapabilities(); + int count = 0; + for (Capability cap : caps) + { + if (count < size+offset) + { + if (count >= offset) + { + result.add(cap); + } + } + else + { + break; + } + count ++; + } + return result; + } + + private NodeRef createRMRootNodeRef() + { + NodeRef root = this.nodeService.getRootNode(SPACES_STORE); + NodeRef filePlan = this.nodeService.createNode(root, ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN, TYPE_FILE_PLAN).getChildRef(); + + return filePlan; + } + + private NodeRef addFilePlanCompoent(NodeRef parent, QName type) + { + String id = GUID.generate(); + String seriesName = "Series" + id; + Map props = new HashMap(2); + props.put(ContentModel.PROP_NAME, seriesName); + props.put(PROP_IDENTIFIER, id); + return nodeService.createNode( + parent, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, seriesName), + type, + props).getChildRef(); + } + + private String createAndAddUserToRole(String role) + { + // Create an athentication + String userName = GUID.generate(); + authenticationService.createAuthentication(userName, "PWD".toCharArray()); + + // Create a person + PropertyMap ppOne = new PropertyMap(4); + ppOne.put(ContentModel.PROP_USERNAME, userName); + ppOne.put(ContentModel.PROP_FIRSTNAME, "firstName"); + ppOne.put(ContentModel.PROP_LASTNAME, "lastName"); + ppOne.put(ContentModel.PROP_EMAIL, "email@email.com"); + ppOne.put(ContentModel.PROP_JOBTITLE, "jobTitle"); + personService.createPerson(ppOne); + + // Assign the new user to the role passed + authorityService.addAuthority(role, userName); + + return userName; + } + + private String createUser() + { + // Create an athentication + String userName = GUID.generate(); + authenticationService.createAuthentication(userName, "PWD".toCharArray()); + + // Create a person + PropertyMap ppOne = new PropertyMap(4); + ppOne.put(ContentModel.PROP_USERNAME, userName); + ppOne.put(ContentModel.PROP_FIRSTNAME, "firstName"); + ppOne.put(ContentModel.PROP_LASTNAME, "lastName"); + ppOne.put(ContentModel.PROP_EMAIL, "email@email.com"); + ppOne.put(ContentModel.PROP_JOBTITLE, "jobTitle"); + personService.createPerson(ppOne); + + return userName; + } + + public void testExecutionAsRMAdmin() + { + final NodeRef filePlan = createRMRootNodeRef(); + + setComplete(); + endTransaction(); + + transactionHelper.doInTransaction(new RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + System.out.println("Groups:"); + Set temp = authorityService.getAllRootAuthorities(AuthorityType.GROUP); + for (String g : temp) + { + System.out.println(" - " + g); + } + System.out.println(""); + + assertTrue(permissionService.hasPermission(filePlan, RMPermissionModel.READ_RECORDS).equals(AccessStatus.ALLOWED)); + assertTrue(permissionService.hasPermission(filePlan, RMPermissionModel.FILE_RECORDS).equals(AccessStatus.ALLOWED)); + assertTrue(permissionService.hasPermission(filePlan, RMPermissionModel.FILING).equals(AccessStatus.ALLOWED)); + + Role adminRole = rmSecurityService.getRole(filePlan, "Administrator"); + assertNotNull(adminRole); + String adminUser = createAndAddUserToRole(adminRole.getRoleGroupName()); + AuthenticationUtil.setFullyAuthenticatedUser(adminUser); + + try + { + assertTrue(permissionService.hasPermission(filePlan, RMPermissionModel.READ_RECORDS).equals(AccessStatus.ALLOWED)); + assertTrue(permissionService.hasPermission(filePlan, RMPermissionModel.FILE_RECORDS).equals(AccessStatus.ALLOWED)); + assertTrue(permissionService.hasPermission(filePlan, RMPermissionModel.FILING).equals(AccessStatus.ALLOWED)); + + // Read the properties of the filePlan + nodeService.getProperties(filePlan); + } + finally + { + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + } + + return null; + } + }); + } + + public void testDefaultRolesBootstrap() + { + NodeRef rootNode = nodeService.getRootNode(SPACES_STORE); + final NodeRef filePlan = nodeService.createNode(rootNode, ContentModel.ASSOC_CHILDREN, + TYPE_FILE_PLAN, + TYPE_FILE_PLAN).getChildRef(); + + setComplete(); + endTransaction(); + + transactionHelper.doInTransaction(new RetryingTransactionCallback() + { + + public Object execute() throws Throwable + { + Set roles = rmSecurityService.getRoles(filePlan); + assertNotNull(roles); + assertEquals(5, roles.size()); + + Role role = rmSecurityService.getRole(filePlan, "User"); + assertNotNull(role); + assertEquals("User", role.getName()); + assertNotNull(role.getDisplayLabel()); + Set caps = role.getCapabilities(); + assertNotNull(caps); + System.out.println("\nUser capabilities: "); + for (String cap : caps) + { + assertNotNull(rmSecurityService.getCapability(cap)); + System.out.println(cap); + } + + role = rmSecurityService.getRole(filePlan, "PowerUser"); + assertNotNull(role); + assertEquals("PowerUser", role.getName()); + assertNotNull(role.getDisplayLabel()); + caps = role.getCapabilities(); + assertNotNull(caps); + System.out.println("\nPowerUser capabilities: "); + for (String cap : caps) + { + assertNotNull(rmSecurityService.getCapability(cap)); + System.out.println(cap); + } + + role = rmSecurityService.getRole(filePlan, "SecurityOfficer"); + assertNotNull(role); + assertEquals("SecurityOfficer", role.getName()); + assertNotNull(role.getDisplayLabel()); + caps = role.getCapabilities(); + assertNotNull(caps); + System.out.println("\nSecurityOfficer capabilities: "); + for (String cap : caps) + { + assertNotNull(rmSecurityService.getCapability(cap)); + System.out.println(cap); + } + + role = rmSecurityService.getRole(filePlan, "RecordsManager"); + assertNotNull(role); + assertEquals("RecordsManager", role.getName()); + assertNotNull(role.getDisplayLabel()); + caps = role.getCapabilities(); + assertNotNull(caps); + System.out.println("\nRecordsManager capabilities: "); + for (String cap : caps) + { + assertNotNull(rmSecurityService.getCapability(cap)); + System.out.println(cap); + } + + role = rmSecurityService.getRole(filePlan, "Administrator"); + assertNotNull(role); + assertEquals("Administrator", role.getName()); + assertNotNull(role.getDisplayLabel()); + caps = role.getCapabilities(); + assertNotNull(caps); + System.out.println("\nAdministrator capabilities: "); + for (String cap : caps) + { + assertNotNull("No capability called " + cap, rmSecurityService.getCapability(cap)); + System.out.println(cap); + } + + return null; + } + + }); + } + + public void xtestCreateNewRMUserAccessToFilePlan() + { + final NodeRef rmRootNode = createRMRootNodeRef(); + + final NodeRef seriesOne = addFilePlanCompoent(rmRootNode, TYPE_RECORD_CATEGORY); + final NodeRef seriesTwo = addFilePlanCompoent(rmRootNode, TYPE_RECORD_CATEGORY); + final NodeRef seriesThree = addFilePlanCompoent(rmRootNode, TYPE_RECORD_CATEGORY); + + final NodeRef catOne = addFilePlanCompoent(seriesOne, TYPE_RECORD_CATEGORY); + final NodeRef catTwo = addFilePlanCompoent(seriesOne, TYPE_RECORD_CATEGORY); + final NodeRef catThree = addFilePlanCompoent(seriesOne, TYPE_RECORD_CATEGORY); + + final NodeRef folderOne = addFilePlanCompoent(catOne, TYPE_RECORD_FOLDER); + final NodeRef folderTwo = addFilePlanCompoent(catOne, TYPE_RECORD_FOLDER); + final NodeRef folderThree = addFilePlanCompoent(catOne, TYPE_RECORD_FOLDER); + + setComplete(); + endTransaction(); + + final String user = transactionHelper.doInTransaction(new RetryingTransactionCallback() + { + public String execute() throws Throwable + { + // Create a new role + Set caps = new HashSet(1); + caps.add(rmSecurityService.getCapability(RMPermissionModel.VIEW_RECORDS)); + + Role role = rmSecurityService.createRole(rmRootNode, "TestRole", "My Test Role", caps); + String user = createUser(); + + // Check the role group and allRole group are set up correctly + Set groups = authorityService.getContainingAuthorities(AuthorityType.GROUP, role.getRoleGroupName(), true); + assertNotNull(groups); + // expect allRole group and the capability group + assertEquals(1, groups.size()); + List tempList = new ArrayList(groups); + assertTrue(tempList.get(0).startsWith("GROUP_AllRoles")); + + // User shouldn't be able to see the file plan node + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Object doWork() throws Exception + { + // Check the permissions of the group on the root node + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(rmRootNode, RMPermissionModel.READ_RECORDS)); + + try + { + nodeService.getChildAssocs(rmRootNode); + fail("The user shouldn't be able to read the children"); + } + catch (AlfrescoRuntimeException e) + { + // expected + } + + return null; + } + }, user); + + // Assign the new user to the role + rmSecurityService.assignRoleToAuthority(rmRootNode, role.getName(), user); + + return user; + } + }); + + transactionHelper.doInTransaction(new RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // Prove that all the series are there + List assocs = nodeService.getChildAssocs(rmRootNode); + assertNotNull(assocs); + assertEquals(3, assocs.size()); + + // User should be able to see the file plan node + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Object doWork() throws Exception + { + // Check user has read on the root + // assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(rmRootNode, RMPermissionModel.READ_RECORDS)); + + // Check that the user can not see any of the series + List assocs = nodeService.getChildAssocs(rmRootNode); + assertNotNull(assocs); + assertEquals(0, assocs.size()); + + return null; + } + }, user); + + // Add read permissions to one of the series + permissionService.setPermission(seriesOne, user, RMPermissionModel.READ_RECORDS, true); + + // Show that user can now see that series + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Object doWork() throws Exception + { + // Check that the user can not see any of the series + List assocs = nodeService.getChildAssocs(rmRootNode); + assertNotNull(assocs); + assertEquals(1, assocs.size()); + + return null; + } + }, user); + + // Add the read permission and file permission to get to the folder + permissionService.setPermission(catOne, user, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(folderOne, user, RMPermissionModel.FILING, true); + + // TODO check visibility of items as we add the permissions + // TODO check that records inherit the permissions ok + + // Try and close the folder as the new user + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Object doWork() throws Exception + { + try + { + rmActionService.executeRecordsManagementAction(folderOne, "closeRecordFolder"); + fail("User does not have the capability for this"); + } + catch (org.alfresco.repo.security.permissions.AccessDeniedException exception) + { + // expected + } + + return null; + } + }, user); + + // Add the capability to the role + Set caps2 = new HashSet(1); + caps2.add(rmSecurityService.getCapability(RMPermissionModel.VIEW_RECORDS)); + caps2.add(rmSecurityService.getCapability(RMPermissionModel.CLOSE_FOLDERS)); + rmSecurityService.updateRole(rmRootNode, "TestRole", "My Test Role", caps2); + + Set aps = permissionService.getAllSetPermissions(rmRootNode); + System.out.println("\nPermissions on new series node: "); + for (AccessPermission ap : aps) + { + System.out.println(" - " + ap.getAuthority() + " has " + ap.getPermission()); + } + + // Try and close the folder as the new user + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Object doWork() throws Exception + { + assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(rmRootNode, RMPermissionModel.CLOSE_FOLDERS)); + + rmActionService.executeRecordsManagementAction(folderOne, "closeRecordFolder"); + + return null; + } + }, user); + + return null; + } + }); + } + + public void testSetPermissions() + { + final NodeRef rmRootNode = createRMRootNodeRef(); + + final NodeRef seriesOne = addFilePlanCompoent(rmRootNode, TYPE_RECORD_CATEGORY); + final NodeRef seriesTwo = addFilePlanCompoent(rmRootNode, TYPE_RECORD_CATEGORY); + final NodeRef seriesThree = addFilePlanCompoent(rmRootNode, TYPE_RECORD_CATEGORY); + + final NodeRef catOne = addFilePlanCompoent(seriesOne, TYPE_RECORD_CATEGORY); + final NodeRef catTwo = addFilePlanCompoent(seriesOne, TYPE_RECORD_CATEGORY); + final NodeRef catThree = addFilePlanCompoent(seriesOne, TYPE_RECORD_CATEGORY); + + final NodeRef folderOne = addFilePlanCompoent(catOne, TYPE_RECORD_FOLDER); + final NodeRef folderTwo = addFilePlanCompoent(catOne, TYPE_RECORD_FOLDER); + final NodeRef folderThree = addFilePlanCompoent(catOne, TYPE_RECORD_FOLDER); + + setComplete(); + endTransaction(); + + transactionHelper.doInTransaction(new RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // Create a new role + Set caps = new HashSet(1); + caps.add(rmSecurityService.getCapability(RMPermissionModel.VIEW_RECORDS)); + + Role role = rmSecurityService.createRole(rmRootNode, "TestRole", "My Test Role", caps); + String user = createUser(); + + rmSecurityService.assignRoleToAuthority(rmRootNode, role.getName(), user); + + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Object doWork() throws Exception + { + assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(rmRootNode, RMPermissionModel.READ_RECORDS)); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(seriesOne, RMPermissionModel.READ_RECORDS)); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(seriesTwo, RMPermissionModel.READ_RECORDS)); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(seriesThree, RMPermissionModel.READ_RECORDS)); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(catOne, RMPermissionModel.READ_RECORDS)); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(catTwo, RMPermissionModel.READ_RECORDS)); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(catThree, RMPermissionModel.READ_RECORDS)); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(folderOne, RMPermissionModel.READ_RECORDS)); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(folderTwo, RMPermissionModel.READ_RECORDS)); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(folderThree, RMPermissionModel.READ_RECORDS)); + + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(rmRootNode, RMPermissionModel.FILING)); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(seriesOne, RMPermissionModel.FILING)); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(seriesTwo, RMPermissionModel.FILING)); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(seriesThree, RMPermissionModel.FILING)); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(catOne, RMPermissionModel.FILING)); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(catTwo, RMPermissionModel.FILING)); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(catThree, RMPermissionModel.FILING)); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(folderOne, RMPermissionModel.FILING)); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(folderTwo, RMPermissionModel.FILING)); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(folderThree, RMPermissionModel.FILING)); + + return null; + } + }, user); + + rmSecurityService.setPermission(catOne, user, RMPermissionModel.FILING); + + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Object doWork() throws Exception + { + assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(rmRootNode, RMPermissionModel.READ_RECORDS)); + assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(seriesOne, RMPermissionModel.READ_RECORDS)); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(seriesTwo, RMPermissionModel.READ_RECORDS)); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(seriesThree, RMPermissionModel.READ_RECORDS)); + assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(catOne, RMPermissionModel.READ_RECORDS)); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(catTwo, RMPermissionModel.READ_RECORDS)); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(catThree, RMPermissionModel.READ_RECORDS)); + assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(folderOne, RMPermissionModel.READ_RECORDS)); + assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(folderTwo, RMPermissionModel.READ_RECORDS)); + assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(folderThree, RMPermissionModel.READ_RECORDS)); + + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(rmRootNode, RMPermissionModel.FILING)); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(seriesOne, RMPermissionModel.FILING)); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(seriesTwo, RMPermissionModel.FILING)); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(seriesThree, RMPermissionModel.FILING)); + assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(catOne, RMPermissionModel.FILING)); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(catTwo, RMPermissionModel.FILING)); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(catThree, RMPermissionModel.FILING)); + assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(folderOne, RMPermissionModel.FILING)); + assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(folderTwo, RMPermissionModel.FILING)); + assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(folderThree, RMPermissionModel.FILING)); + + return null; + } + }, user); + + return null; + } + }); + } +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementServiceImplTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementServiceImplTest.java new file mode 100644 index 0000000000..f9d818bdaa --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementServiceImplTest.java @@ -0,0 +1,670 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.service; + +import java.util.List; +import java.util.Set; + +import org.alfresco.module.org_alfresco_module_rm.FilePlanComponentKind; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.dod5015.DOD5015Model; +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.GUID; +import org.springframework.util.CollectionUtils; + + +/** + * Records management service test. + * + * @author Roy Wetherall + */ +public class RecordsManagementServiceImplTest extends BaseRMTestCase +{ + /********** RM Component methods **********/ + + /** + * @see RecordsManagementService#isFilePlanComponent(org.alfresco.service.cmr.repository.NodeRef) + */ + public void testIsFilePlanComponent() throws Exception + { + doTestInTransaction(new Test() + { + @Override + public NodeRef run() + { + assertTrue("The rm root container should be a rm component", rmService.isFilePlanComponent(filePlan)); + assertTrue("The rm container should be a rm component", rmService.isFilePlanComponent(rmContainer)); + assertTrue("The rm folder should be a rm component", rmService.isFilePlanComponent(rmFolder)); + + return null; + } + }); + } + + /** + * @see RecordsManagementService#getFilePlanComponentKind(NodeRef) + */ + public void testGetFilePlanComponentKind() throws Exception + { + doTestInTransaction(new Test() + { + @Override + public NodeRef run() throws Exception + { + return utils.createRecord(rmFolder, "testRecord.txt"); + } + + @Override + public void test(NodeRef result) throws Exception + { + assertEquals(FilePlanComponentKind.FILE_PLAN, rmService.getFilePlanComponentKind(filePlan)); + assertEquals(FilePlanComponentKind.RECORD_CATEGORY, rmService.getFilePlanComponentKind(rmContainer)); + assertEquals(FilePlanComponentKind.RECORD_FOLDER, rmService.getFilePlanComponentKind(rmFolder)); + assertEquals(FilePlanComponentKind.RECORD, rmService.getFilePlanComponentKind(result)); + // TODO HOLD and TRANSFER + assertNull(rmService.getFilePlanComponentKind(folder)); + } + }); + } + + /** + * @see RecordsManagementService#isFilePlan(NodeRef) + */ + public void testIsFilePlan() throws Exception + { + doTestInTransaction(new Test() + { + @Override + public NodeRef run() + { + assertTrue("This is a records management root", rmService.isFilePlan(filePlan)); + assertFalse("This should not be a records management root", rmService.isFilePlan(rmContainer)); + assertFalse("This should not be a records management root", rmService.isFilePlan(rmFolder)); + + return null; + } + }); + } + + /** + * @see RecordsManagementService#isRecordCategory(NodeRef) + */ + public void testIsRecordCategory() throws Exception + { + doTestInTransaction(new Test() + { + @Override + public NodeRef run() + { + assertFalse("This should not be a record category.", rmService.isRecordCategory(filePlan)); + assertTrue("This is a record category.", rmService.isRecordCategory(rmContainer)); + assertFalse("This should not be a record category.", rmService.isRecordCategory(rmFolder)); + + return null; + } + }); + } + + /** + * @see RecordsManagementService#isRecordFolder(NodeRef) + */ + public void testIsRecordFolder() throws Exception + { + doTestInTransaction(new Test() + { + @Override + public NodeRef run() + { + assertFalse("This should not be a record folder", rmService.isRecordFolder(filePlan)); + assertFalse("This should not be a record folder", rmService.isRecordFolder(rmContainer)); + assertTrue("This should be a record folder", rmService.isRecordFolder(rmFolder)); + + return null; + } + }); + } + + // TODO void testIsRecord() + + // TODO void testIsHold() + + // TODO void testIsTransfer() + + // TODO void testGetNodeRefPath() + + /** + * @see RecordsManagementService#getRecordsManagementRoot() + */ + public void testGetRecordsManagementRoot() throws Exception + { + doTestInTransaction(new Test() + { + @Override + public NodeRef run() + { + assertEquals(filePlan, rmService.getFilePlan(filePlan)); + assertEquals(filePlan, rmService.getFilePlan(rmContainer)); + assertEquals(filePlan, rmService.getFilePlan(rmFolder)); + + return null; + } + }); + } + + /********** Record Management Root methods **********/ + + /** + * @see RecordsManagementService#getFilePlans() + */ + public void testGetRecordsManagementRoots() throws Exception + { + doTestInTransaction(new Test() + { + @Override + public NodeRef run() + { +// List roots = rmService.getRecordsManagementRoots(storeRef); +// assertNotNull(roots); +// assertTrue(roots.size() != 0); +// assertTrue(roots.contains(rmRootContainer)); +// +// RecordsManagementServiceImpl temp = (RecordsManagementServiceImpl)applicationContext.getBean("recordsManagementService"); +// temp.setDefaultStoreRef(storeRef); + + List roots = rmService.getFilePlans(); + assertNotNull(roots); + assertTrue(roots.size() != 0); + assertTrue(roots.contains(filePlan)); + + return null; + } + }); + } + + /** + * @see RecordsManagementService#createFilePlan(org.alfresco.service.cmr.repository.NodeRef, String) + * @see RecordsManagementService#createFilePlan(org.alfresco.service.cmr.repository.NodeRef, String, org.alfresco.service.namespace.QName) + */ + public void testCreateFilePlan() throws Exception + { + // Create default type of root + doTestInTransaction(new Test() + { + @Override + public NodeRef run() + { + String id = setString("id", GUID.generate()); + return rmService.createFilePlan(folder, id); + } + + @Override + public void test(NodeRef result) + { + assertNotNull("Unable to create records management root", result); + basicRMContainerCheck(result, getString("id"), TYPE_FILE_PLAN); + } + }); + + // Create specific type of root + doTestInTransaction(new Test() + { + @Override + public NodeRef run() + { + String id = setString("id", GUID.generate()); + return rmService.createFilePlan(folder, id, TYPE_FILE_PLAN); + } + + @Override + public void test(NodeRef result) + { + assertNotNull("Unable to create records management root", result); + basicRMContainerCheck(result, getString("id"), TYPE_FILE_PLAN); + } + }); + + // Failure: creating root in existing hierarchy + doTestInTransaction(new FailureTest() + { + @Override + public void run() + { + rmService.createFilePlan(rmContainer, GUID.generate()); + } + }); + + // Failure: type no extended from root container + doTestInTransaction(new FailureTest() + { + @Override + public void run() + { + rmService.createFilePlan(folder, GUID.generate(), TYPE_FOLDER); + } + }); + } + + /********** Records Management Container methods **********/ + + /** + * @see RecordsManagementService#createRecordCategory(NodeRef, String) + * @see RecordsManagementService#createRecordCategory(NodeRef, String, org.alfresco.service.namespace.QName) + */ + public void testCreateRecordCategory() throws Exception + { + // Create container (in root) + doTestInTransaction(new Test() + { + @Override + public NodeRef run() + { + String id = setString("id", GUID.generate()); + return rmService.createRecordCategory(filePlan, id); + } + + @Override + public void test(NodeRef result) + { + assertNotNull("Unable to create records management container", result); + basicRMContainerCheck(result, getString("id"), TYPE_RECORD_CATEGORY); + } + }); + + // Create container (in container) + doTestInTransaction(new Test() + { + @Override + public NodeRef run() + { + String id = setString("id", GUID.generate()); + return rmService.createRecordCategory(rmContainer, id); + } + + @Override + public void test(NodeRef result) + { + assertNotNull("Unable to create records management container", result); + basicRMContainerCheck(result, getString("id"), TYPE_RECORD_CATEGORY); + } + }); + + // TODO need a custom type of container! + // Create container of a given type +// doTestInTransaction(new Test() +// { +// @Override +// public NodeRef run() +// { +// String id = setString("id", GUID.generate()); +// return rmService.createRecordCategory(filePlan, id, TYPE_RECORD_SERIES); +// } +// +// @Override +// public void test(NodeRef result) +// { +// assertNotNull("Unable to create records management container", result); +// basicRMContainerCheck(result, getString("id"), TYPE_RECORD_SERIES); +// } +// }); + + // Fail Test: parent is not a container + doTestInTransaction(new FailureTest() + { + @Override + public void run() + { + rmService.createRecordCategory(folder, GUID.generate()); + } + }); + + // Fail Test: type is not a sub-type of rm:recordsManagementContainer + doTestInTransaction(new FailureTest() + { + @Override + public void run() + { + rmService.createRecordCategory(filePlan, GUID.generate(), TYPE_FOLDER); + } + }); + } + + /** + * @see RecordsManagementService#getAllContained(NodeRef) + * @see RecordsManagementService#getAllContained(NodeRef, boolean) + */ + public void testGetAllContained() throws Exception + { + // Get all contained test + doTestInTransaction(new Test() + { + @Override + public Void run() + { + // Add to the test data + NodeRef series = rmService.createRecordCategory(rmContainer, "rmSeries"); + NodeRef seriesChildFolder = rmService.createRecordFolder(series, "seriesRecordFolder"); + NodeRef seriesChildContainer = rmService.createRecordCategory(series, "childContainer"); + + // Put in model + setNodeRef("series", series); + setNodeRef("seriesChildFolder", seriesChildFolder); + setNodeRef("seriesChildContainer", seriesChildContainer); + + return null; + } + + @Override + public void test(Void result) throws Exception + { + List nodes = rmService.getAllContained(rmContainer); + assertNotNull(nodes); + assertEquals(2, nodes.size()); + assertTrue(nodes.contains(getNodeRef("series"))); + assertTrue(nodes.contains(rmFolder)); + + nodes = rmService.getAllContained(rmContainer, false); + assertNotNull(nodes); + assertEquals(2, nodes.size()); + assertTrue(nodes.contains(getNodeRef("series"))); + assertTrue(nodes.contains(rmFolder)); + + nodes = rmService.getAllContained(rmContainer, true); + assertNotNull(nodes); + assertEquals(4, nodes.size()); + assertTrue(nodes.contains(getNodeRef("series"))); + assertTrue(nodes.contains(rmFolder)); + assertTrue(nodes.contains(getNodeRef("seriesChildFolder"))); + assertTrue(nodes.contains(getNodeRef("seriesChildContainer"))); + + } + }); + + // Failure: call on record folder + doTestInTransaction(new FailureTest() + { + @Override + public void run() + { + rmService.getAllContained(rmFolder); + } + }); + } + + /** + * @see RecordsManagementService#getContainedRecordCategories(NodeRef) + * @see RecordsManagementService#getContainedRecordCategories(NodeRef, boolean) + */ + public void testGetContainedRecordCategories() throws Exception + { + // Test getting all contained containers + doTestInTransaction(new Test() + { + @Override + public Void run() + { + // Add to the test data + NodeRef series = rmService.createRecordCategory(rmContainer, "rmSeries"); + NodeRef seriesChildFolder = rmService.createRecordFolder(series, "seriesRecordFolder"); + NodeRef seriesChildContainer = rmService.createRecordCategory(series, "childContainer"); + + // Put in model + setNodeRef("series", series); + setNodeRef("seriesChildFolder", seriesChildFolder); + setNodeRef("seriesChildContainer", seriesChildContainer); + + return null; + } + + @Override + public void test(Void result) throws Exception + { + List nodes = rmService.getContainedRecordCategories(rmContainer); + assertNotNull(nodes); + assertEquals(1, nodes.size()); + assertTrue(nodes.contains(getNodeRef("series"))); + + nodes = rmService.getContainedRecordCategories(rmContainer, false); + assertNotNull(nodes); + assertEquals(1, nodes.size()); + assertTrue(nodes.contains(getNodeRef("series"))); + + nodes = rmService.getContainedRecordCategories(rmContainer, true); + assertNotNull(nodes); + assertEquals(2, nodes.size()); + assertTrue(nodes.contains(getNodeRef("series"))); + assertTrue(nodes.contains(getNodeRef("seriesChildContainer"))); + } + }); + + // Failure: call on record folder + doTestInTransaction(new FailureTest() + { + @Override + public void run() + { + rmService.getContainedRecordCategories(rmFolder); + } + }); + } + + /** + * @see RecordsManagementService#getContainedRecordFolders(NodeRef) + * @see RecordsManagementService#getContainedRecordFolders(NodeRef, boolean) + */ + public void testGetContainedRecordFolders() throws Exception + { + // Test getting all contained record folders + doTestInTransaction(new Test() + { + @Override + public Void run() + { + // Add to the test data + NodeRef series = rmService.createRecordCategory(rmContainer, "rmSeries"); + NodeRef seriesChildFolder = rmService.createRecordFolder(series, "seriesRecordFolder"); + NodeRef seriesChildContainer = rmService.createRecordCategory(series, "childContainer"); + + // Put in model + setNodeRef("series", series); + setNodeRef("seriesChildFolder", seriesChildFolder); + setNodeRef("seriesChildContainer", seriesChildContainer); + + return null; + } + + @Override + public void test(Void result) throws Exception + { + List nodes = rmService.getContainedRecordFolders(rmContainer); + assertNotNull(nodes); + assertEquals(1, nodes.size()); + assertTrue(nodes.contains(rmFolder)); + + nodes = rmService.getContainedRecordFolders(rmContainer, false); + assertNotNull(nodes); + assertEquals(1, nodes.size()); + assertTrue(nodes.contains(rmFolder)); + + nodes = rmService.getContainedRecordFolders(rmContainer, true); + assertNotNull(nodes); + assertEquals(2, nodes.size()); + assertTrue(nodes.contains(rmFolder)); + assertTrue(nodes.contains(getNodeRef("seriesChildFolder"))); + } + }); + + // Failure: call on record folder + doTestInTransaction(new FailureTest() + { + @Override + public void run() + { + rmService.getContainedRecordFolders(rmFolder); + } + }); + } + + /********** Record Folder methods **********/ + + // TODO void testIsRecordFolderDeclared() + + // TODO void testIsRecordFolderClosed() + + // TODO void testGetRecords() + + /** + * @see RecordsManagementService#createRecordFolder(NodeRef, String) + * @see RecordsManagementService#createRecordFolder(NodeRef, String, QName) + */ + public void testCreateRecordFolder() throws Exception + { + // Create record + doTestInTransaction(new Test() + { + @Override + public NodeRef run() + { + String id = setString("id", GUID.generate()); + return rmService.createRecordFolder(rmContainer, id); + } + + @Override + public void test(NodeRef result) + { + assertNotNull("Unable to create record folder", result); + basicRMContainerCheck(result, getString("id"), TYPE_RECORD_FOLDER); + } + }); + + // TODO Create record of type + + // Failure: Create record with invalid type + doTestInTransaction(new FailureTest() + { + @Override + public void run() + { + rmService.createRecordFolder(rmContainer, GUID.generate(), TYPE_FOLDER); + } + }); + + // Failure: Create record folder in root + doTestInTransaction(new FailureTest() + { + @Override + public void run() + { + rmService.createRecordFolder(filePlan, GUID.generate()); + } + }); + } + + /********** Record methods **********/ + + // TODO void testIsRecordFrozen() + + /** + * @see RecordsManagementService#getRecordMetaDataAspects() + */ + public void testGetRecordMetaDataAspects() + { + doTestInTransaction(new Test() + { + @Override + public Void run() + { + Set aspects = rmService.getRecordMetaDataAspects(); + assertNotNull(aspects); + assertEquals(5, aspects.size()); + assertTrue(aspects.containsAll( + CollectionUtils.arrayToList(new QName[] + { + DOD5015Model.ASPECT_DIGITAL_PHOTOGRAPH_RECORD, + DOD5015Model.ASPECT_PDF_RECORD, + DOD5015Model.ASPECT_WEB_RECORD, + DOD5015Model.ASPECT_SCANNED_RECORD, + ASPECT_RECORD_META_DATA + }))); + + return null; + } + }); + } + + // TODO void testGetRecordFolders(NodeRef record); + + // TODO void testIsRecordDeclared(NodeRef nodeRef); + + /********** RM2 - Multi-hierarchy record taxonomy's **********/ + + /** + * Test to create a simple multi-hierarchy record taxonomy + */ + public void testCreateSimpleHierarchy() + { + doTestInTransaction(new Test() + { + @Override + public Void run() + { + // Create 3 level hierarchy + NodeRef levelOne = setNodeRef("container1", rmService.createRecordCategory(filePlan, "container1")); + assertNotNull("Unable to create container", levelOne); + NodeRef levelTwo = setNodeRef("container2", rmService.createRecordCategory(levelOne, "container2")); + assertNotNull("Unable to create container", levelTwo); + NodeRef levelThree = setNodeRef("container3", rmService.createRecordCategory(levelTwo, "container3")); + assertNotNull("Unable to create container", levelThree); + NodeRef levelThreeRecordFolder = setNodeRef("recordFolder3", rmService.createRecordFolder(levelThree, "recordFolder3")); + assertNotNull("Unable to create record folder", levelThreeRecordFolder); + + return null; + } + + @Override + public void test(Void result) + { + // Test that the hierarchy has been created correctly + basicRMContainerCheck(getNodeRef("container1"), "container1", TYPE_RECORD_CATEGORY); + basicRMContainerCheck(getNodeRef("container2"), "container2", TYPE_RECORD_CATEGORY); + basicRMContainerCheck(getNodeRef("container3"), "container3", TYPE_RECORD_CATEGORY); + basicRMContainerCheck(getNodeRef("recordFolder3"), "recordFolder3", TYPE_RECORD_FOLDER); + + // TODO need to check that the parents and children can be retrieved correctly + } + }); + } + + /** + * A basic test of a records management container + * + * @param nodeRef node reference + * @param name name of the container + * @param type the type of container + */ + private void basicRMContainerCheck(NodeRef nodeRef, String name, QName type) + { + // Check the basic details + assertEquals(name, nodeService.getProperty(nodeRef, PROP_NAME)); + assertNotNull("RM id has not been set", nodeService.getProperty(nodeRef, PROP_IDENTIFIER)); + assertEquals(type, nodeService.getType(nodeRef)); + } + +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/VitalRecordServiceImplTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/VitalRecordServiceImplTest.java new file mode 100644 index 0000000000..ce07978310 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/VitalRecordServiceImplTest.java @@ -0,0 +1,381 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.service; + +import java.util.Date; + +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementSearchBehaviour; +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; +import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordDefinition; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.Period; +import org.alfresco.util.GUID; + +/** + * Vital record service implementation unit test. + * + * @author Roy Wetherall + */ +public class VitalRecordServiceImplTest extends BaseRMTestCase +{ + /** Test periods */ + protected static final Period PERIOD_NONE = new Period("none|0"); + protected static final Period PERIOD_WEEK = new Period("week|1"); + protected static final Period PERIOD_MONTH = new Period("month|1"); + + /** Test records */ + private NodeRef mhRecord51; + private NodeRef mhRecord52; + private NodeRef mhRecord53; + private NodeRef mhRecord54; + private NodeRef mhRecord55; + + /** Indicate this is a multi hierarchy test */ + @Override + protected boolean isMultiHierarchyTest() + { + return true; + } + + /** vital record multi-hierarchy test data + * + * |--rmRootContainer (no vr def) + * | + * |--mhContainer (no vr def) + * | + * |--mhContainer-1-1 (has schedule - folder level) (no vr def) + * | | + * | |--mhContainer-2-1 (vr def) + * | | + * | |--mhContainer-3-1 (no vr def) + * | + * |--mhContainer-1-2 (has schedule - folder level) (no vr def) + * | + * |--mhContainer-2-2 (no vr def) + * | | + * | |--mhContainer-3-2 (vr def disabled) + * | | + * | |--mhContainer-3-3 (has schedule - record level) (vr def) + * | + * |--mhContainer-2-3 (has schedule - folder level) (vr def) + * | + * |--mhContainer-3-4 (no vr def) + * | + * |--mhContainer-3-5 (has schedule- record level) (vr def) + */ + @Override + protected void setupMultiHierarchyTestData() + { + // Load core test data + super.setupMultiHierarchyTestData(); + + // Setup vital record definitions + setupVitalRecordDefinition(mhContainer21, true, PERIOD_WEEK); + setupVitalRecordDefinition(mhContainer32, false, PERIOD_WEEK); + setupVitalRecordDefinition(mhContainer33, true, PERIOD_WEEK); + setupVitalRecordDefinition(mhContainer23, true, PERIOD_WEEK); + setupVitalRecordDefinition(mhContainer35, true, PERIOD_MONTH); + + // Create records + retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + mhRecord51 = utils.createRecord(mhRecordFolder41, "record51.txt"); + mhRecord52 = utils.createRecord(mhRecordFolder42, "record52.txt"); + mhRecord53 = utils.createRecord(mhRecordFolder43, "record53.txt"); + mhRecord54 = utils.createRecord(mhRecordFolder44, "record54.txt"); + mhRecord55 = utils.createRecord(mhRecordFolder45, "record55.txt"); + + return null; + } + }); + } + + /** + * Helper to set up the vital record definition data in a transactional manner. + * + * @param nodeRef + * @param enabled + * @param period + */ + private void setupVitalRecordDefinition(final NodeRef nodeRef, final boolean enabled, final Period period) + { + retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + vitalRecordService.setVitalRecordDefintion(nodeRef, enabled, period); + return null; + } + }); + } + + /** + * Based on the initial data: + * - check category, folder and record raw values. + * - check search aspect values. + */ + public void testInit() + { + doTestInTransaction(new Test() + { + @Override + public Void run() + { + assertHasVitalRecordDefinition(mhContainer, false, null); + assertHasVitalRecordDefinition(mhContainer11, false, null); + assertHasVitalRecordDefinition(mhContainer12, false, null); + assertHasVitalRecordDefinition(mhContainer21, true, PERIOD_WEEK); + assertHasVitalRecordDefinition(mhContainer22, false, null); + assertHasVitalRecordDefinition(mhContainer23, true, PERIOD_WEEK); + assertHasVitalRecordDefinition(mhContainer31, true, PERIOD_WEEK); + assertHasVitalRecordDefinition(mhContainer32, false, PERIOD_WEEK); + assertHasVitalRecordDefinition(mhContainer33, true, PERIOD_WEEK); + assertHasVitalRecordDefinition(mhContainer34, true, PERIOD_WEEK); + assertHasVitalRecordDefinition(mhContainer35, true, PERIOD_MONTH); + + assertHasVitalRecordDefinition(mhRecordFolder41, true, PERIOD_WEEK); + assertHasVitalRecordDefinition(mhRecordFolder42, false, null); + assertHasVitalRecordDefinition(mhRecordFolder43, true, PERIOD_WEEK); + assertHasVitalRecordDefinition(mhRecordFolder44, true, PERIOD_WEEK); + assertHasVitalRecordDefinition(mhRecordFolder45, true, PERIOD_MONTH); + + assertVitalRecord(mhRecord51, true, PERIOD_WEEK); + assertVitalRecord(mhRecord52, false, null); + assertVitalRecord(mhRecord53, true, PERIOD_WEEK); + assertVitalRecord(mhRecord54, true, PERIOD_WEEK); + assertVitalRecord(mhRecord55, true, PERIOD_MONTH); + + return null; + } + }); + } + + /** + * Test that when new record categories and record folders are created in an existing file plan + * structure that they correctly inherit the correct vital record property values + */ + public void testValueInheritance() throws Exception + { + // Test record category value inheritance + doTestInTransaction(new Test() + { + @Override + public NodeRef run() + { + return rmService.createRecordCategory(mhContainer35, GUID.generate()); + } + + @Override + public void test(NodeRef result) throws Exception + { + assertHasVitalRecordDefinition(result, true, PERIOD_MONTH); + } + }); + + // Test record folder value inheritance + doTestInTransaction(new Test() + { + @Override + public NodeRef run() + { + return rmService.createRecordFolder(mhContainer32, GUID.generate()); + } + + @Override + public void test(NodeRef result) throws Exception + { + assertHasVitalRecordDefinition(result, false, PERIOD_WEEK); + } + }); + } + + /** + * Test to ensure that changes made to vital record definitions are reflected down the hierarchy. + */ + public void testChangesToVitalRecordDefinitions() throws Exception + { + // Override vital record definition + doTestInTransaction(new Test() + { + @Override + public Void run() + { + setupVitalRecordDefinition(mhContainer31, true, PERIOD_MONTH); + return null; + } + + @Override + public void test(Void result) throws Exception + { + assertHasVitalRecordDefinition(mhContainer, false, null); + assertHasVitalRecordDefinition(mhContainer11, false, null); + assertHasVitalRecordDefinition(mhContainer12, false, null); + assertHasVitalRecordDefinition(mhContainer21, true, PERIOD_WEEK); + assertHasVitalRecordDefinition(mhContainer22, false, null); + assertHasVitalRecordDefinition(mhContainer23, true, PERIOD_WEEK); + assertHasVitalRecordDefinition(mhContainer31, true, PERIOD_MONTH); + assertHasVitalRecordDefinition(mhContainer32, false, PERIOD_WEEK); + assertHasVitalRecordDefinition(mhContainer33, true, PERIOD_WEEK); + assertHasVitalRecordDefinition(mhContainer34, true, PERIOD_WEEK); + assertHasVitalRecordDefinition(mhContainer35, true, PERIOD_MONTH); + + assertHasVitalRecordDefinition(mhRecordFolder41, true, PERIOD_MONTH); + assertHasVitalRecordDefinition(mhRecordFolder42, false, null); + assertHasVitalRecordDefinition(mhRecordFolder43, true, PERIOD_WEEK); + assertHasVitalRecordDefinition(mhRecordFolder44, true, PERIOD_WEEK); + assertHasVitalRecordDefinition(mhRecordFolder45, true, PERIOD_MONTH); + + assertVitalRecord(mhRecord51, true, PERIOD_MONTH); + assertVitalRecord(mhRecord52, false, null); + assertVitalRecord(mhRecord53, true, PERIOD_WEEK); + assertVitalRecord(mhRecord54, true, PERIOD_WEEK); + assertVitalRecord(mhRecord55, true, PERIOD_MONTH); + } + }); + + // 'turn off' vital record def + doTestInTransaction(new Test() + { + @Override + public Void run() + { + setupVitalRecordDefinition(mhContainer31, false, PERIOD_NONE); + return null; + } + + @Override + public void test(Void result) throws Exception + { + assertHasVitalRecordDefinition(mhContainer, false, null); + assertHasVitalRecordDefinition(mhContainer11, false, null); + assertHasVitalRecordDefinition(mhContainer12, false, null); + assertHasVitalRecordDefinition(mhContainer21, true, PERIOD_WEEK); + assertHasVitalRecordDefinition(mhContainer22, false, null); + assertHasVitalRecordDefinition(mhContainer23, true, PERIOD_WEEK); + assertHasVitalRecordDefinition(mhContainer31, false, null); + assertHasVitalRecordDefinition(mhContainer32, false, PERIOD_WEEK); + assertHasVitalRecordDefinition(mhContainer33, true, PERIOD_WEEK); + assertHasVitalRecordDefinition(mhContainer34, true, PERIOD_WEEK); + assertHasVitalRecordDefinition(mhContainer35, true, PERIOD_MONTH); + + assertHasVitalRecordDefinition(mhRecordFolder41, false, null); + assertHasVitalRecordDefinition(mhRecordFolder42, false, null); + assertHasVitalRecordDefinition(mhRecordFolder43, true, PERIOD_WEEK); + assertHasVitalRecordDefinition(mhRecordFolder44, true, PERIOD_WEEK); + assertHasVitalRecordDefinition(mhRecordFolder45, true, PERIOD_MONTH); + + assertVitalRecord(mhRecord51, false, null); + assertVitalRecord(mhRecord52, false, null); + assertVitalRecord(mhRecord53, true, PERIOD_WEEK); + assertVitalRecord(mhRecord54, true, PERIOD_WEEK); + assertVitalRecord(mhRecord55, true, PERIOD_MONTH); + } + }); + + // Test parent change overrites existing + doTestInTransaction(new Test() + { + @Override + public Void run() + { + setupVitalRecordDefinition(mhContainer12, true, PERIOD_MONTH); + return null; + } + + @Override + public void test(Void result) throws Exception + { + assertHasVitalRecordDefinition(mhContainer, false, null); + assertHasVitalRecordDefinition(mhContainer11, false, null); + assertHasVitalRecordDefinition(mhContainer12, true, PERIOD_MONTH); + assertHasVitalRecordDefinition(mhContainer21, true, PERIOD_WEEK); + assertHasVitalRecordDefinition(mhContainer22, true, PERIOD_MONTH); + assertHasVitalRecordDefinition(mhContainer23, true, PERIOD_MONTH); + assertHasVitalRecordDefinition(mhContainer31, false, null); + assertHasVitalRecordDefinition(mhContainer32, true, PERIOD_MONTH); + assertHasVitalRecordDefinition(mhContainer33, true, PERIOD_MONTH); + assertHasVitalRecordDefinition(mhContainer34, true, PERIOD_MONTH); + assertHasVitalRecordDefinition(mhContainer35, true, PERIOD_MONTH); + + assertHasVitalRecordDefinition(mhRecordFolder41, false, null); + assertHasVitalRecordDefinition(mhRecordFolder42, true, PERIOD_MONTH); + assertHasVitalRecordDefinition(mhRecordFolder43, true, PERIOD_MONTH); + assertHasVitalRecordDefinition(mhRecordFolder44, true, PERIOD_MONTH); + assertHasVitalRecordDefinition(mhRecordFolder45, true, PERIOD_MONTH); + + assertVitalRecord(mhRecord51, false, null); + assertVitalRecord(mhRecord52, true, PERIOD_MONTH); + assertVitalRecord(mhRecord53, true, PERIOD_MONTH); + assertVitalRecord(mhRecord54, true, PERIOD_MONTH); + assertVitalRecord(mhRecord55, true, PERIOD_MONTH); + } + }); + + } + + @SuppressWarnings("deprecation") + private void assertHasVitalRecordDefinition(NodeRef nodeRef, boolean enabled, Period period) + { + assertTrue(nodeService.hasAspect(nodeRef, ASPECT_VITAL_RECORD_DEFINITION)); + + VitalRecordDefinition def = vitalRecordService.getVitalRecordDefinition(nodeRef); + assertNotNull(def); + + Boolean vitalRecordIndicator = (Boolean)nodeService.getProperty(nodeRef, PROP_VITAL_RECORD_INDICATOR); + assertNotNull(vitalRecordIndicator); + assertEquals(enabled, vitalRecordIndicator.booleanValue()); + assertEquals(enabled, def.isEnabled()); + + if (enabled == true) + { + Period reviewPeriod = (Period)nodeService.getProperty(nodeRef, PROP_REVIEW_PERIOD); + assertNotNull(reviewPeriod); + assertEquals(period, reviewPeriod); + assertEquals(period, def.getReviewPeriod()); + assertEquals(period.getNextDate(new Date()).getDate(), def.getNextReviewDate().getDate()); + } + } + + @SuppressWarnings("deprecation") + private void assertVitalRecord(NodeRef nodeRef, boolean enabled, Period period) + { + assertEquals(enabled, nodeService.hasAspect(nodeRef, ASPECT_VITAL_RECORD)); + if (enabled == true) + { + Date reviewAsOf = (Date)nodeService.getProperty(nodeRef, PROP_REVIEW_AS_OF); + assertNotNull(reviewAsOf); + assertEquals(period.getNextDate(new Date()).getDate(), reviewAsOf.getDate()); + + assertEquals(period.getPeriodType(), nodeService.getProperty(nodeRef, RecordsManagementSearchBehaviour.PROP_RS_VITAL_RECORD_REVIEW_PERIOD)); + assertEquals(period.getExpression(), nodeService.getProperty(nodeRef, RecordsManagementSearchBehaviour.PROP_RS_VITAL_RECORD_REVIEW_PERIOD_EXPRESSION)); + } + else + { + assertNull(nodeService.getProperty(nodeRef, RecordsManagementSearchBehaviour.PROP_RS_VITAL_RECORD_REVIEW_PERIOD)); + assertNull(nodeService.getProperty(nodeRef, RecordsManagementSearchBehaviour.PROP_RS_VITAL_RECORD_REVIEW_PERIOD_EXPRESSION)); + } + } +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/system/CapabilitiesSystemTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/system/CapabilitiesSystemTest.java new file mode 100644 index 0000000000..55d7ced40e --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/system/CapabilitiesSystemTest.java @@ -0,0 +1,8849 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.system; + +import java.io.Serializable; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.transaction.Status; +import javax.transaction.UserTransaction; + +import junit.framework.TestCase; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; +import org.alfresco.module.org_alfresco_module_rm.action.impl.CompleteEventAction; +import org.alfresco.module.org_alfresco_module_rm.action.impl.FreezeAction; +import org.alfresco.module.org_alfresco_module_rm.action.impl.TransferAction; +import org.alfresco.module.org_alfresco_module_rm.action.impl.TransferCompleteAction; +import org.alfresco.module.org_alfresco_module_rm.capability.Capability; +import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService; +import org.alfresco.module.org_alfresco_module_rm.capability.RMEntryVoter; +import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.permissions.AccessDeniedException; +import org.alfresco.repo.security.permissions.PermissionReference; +import org.alfresco.repo.security.permissions.impl.model.PermissionModel; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.security.AccessPermission; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.view.ImporterBinding; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.ApplicationContextHelper; +import org.springframework.context.ApplicationContext; + +/** + * @author andyh + */ +public class CapabilitiesSystemTest extends TestCase implements RecordsManagementModel +{ + + private ApplicationContext ctx; + + private NodeRef rootNodeRef; + + private NodeService nodeService; + + private NodeService publicNodeService; + + private TransactionService transactionService; + + private UserTransaction testTX; + + private NodeRef filePlan; + + private PermissionService permissionService; + + private RecordsManagementService recordsManagementService; + + private RecordsManagementSecurityService recordsManagementSecurityService; + + private RecordsManagementActionService recordsManagementActionService; + + private RecordsManagementEventService recordsManagementEventService; + + private CapabilityService capabilityService; + + private PermissionModel permissionModel; + + private ContentService contentService; + + private NodeRef recordSeries; + + private NodeRef recordCategory_1; + + private NodeRef recordCategory_2; + + private NodeRef recordFolder_1; + + private NodeRef recordFolder_2; + + private NodeRef record_1; + + private NodeRef record_2; + + private RMEntryVoter rmEntryVoter; + + private AuthorityService authorityService; + + private String rmUsers; + + private String rmPowerUsers; + + private String rmSecurityOfficers; + + private String rmRecordsManagers; + + private String rmAdministrators; + + private PersonService personService; + + private String rm_user; + + private String rm_power_user; + + private String rm_security_officer; + + private String rm_records_manager; + + private String rm_administrator; + + private String test_user; + + private String testers; + + private NodeRef recordCategory_3; + + private NodeRef recordFolder_3; + + private NodeRef record_3; + + private ContentService publicContentService; + + /** + * @param name + */ + public CapabilitiesSystemTest(String name) + { + super(name); + } + + /* + * (non-Javadoc) + * + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception + { + ctx = ApplicationContextHelper.getApplicationContext(); + + super.setUp(); + + nodeService = (NodeService) ctx.getBean("dbNodeService"); + publicNodeService = (NodeService) ctx.getBean("NodeService"); + transactionService = (TransactionService) ctx.getBean("transactionComponent"); + permissionService = (PermissionService) ctx.getBean("permissionService"); + permissionModel = (PermissionModel) ctx.getBean("permissionsModelDAO"); + contentService = (ContentService) ctx.getBean("contentService"); + publicContentService = (ContentService) ctx.getBean("ContentService"); + authorityService = (AuthorityService) ctx.getBean("authorityService"); + personService = (PersonService) ctx.getBean("personService"); + capabilityService = (CapabilityService) ctx.getBean("CapabilityService"); + + recordsManagementService = (RecordsManagementService) ctx.getBean("RecordsManagementService"); + recordsManagementSecurityService = (RecordsManagementSecurityService) ctx.getBean("RecordsManagementSecurityService"); + recordsManagementActionService = (RecordsManagementActionService) ctx.getBean("RecordsManagementActionService"); + recordsManagementEventService = (RecordsManagementEventService) ctx.getBean("RecordsManagementEventService"); + rmEntryVoter = (RMEntryVoter) ctx.getBean("rmEntryVoter"); + + testTX = transactionService.getUserTransaction(); + testTX.begin(); + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + + StoreRef storeRef = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis()); + rootNodeRef = nodeService.getRootNode(storeRef); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + + recordsManagementEventService.getEvents(); + recordsManagementEventService.addEvent("rmEventType.simple", "event", "My Event"); + + filePlan = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, TYPE_FILE_PLAN, TYPE_FILE_PLAN).getChildRef(); + recordSeries = createRecordSeries(filePlan, "RS", "RS-1", "Record Series", "My record series"); + recordCategory_1 = createRecordCategory(recordSeries, "Docs", "101-1", "Docs", "Docs", "week|1", true, false); + recordCategory_2 = createRecordCategory(recordSeries, "More Docs", "101-2", "More Docs", "More Docs", "week|1", true, true); + recordCategory_3 = createRecordCategory(recordSeries, "No disp schedule", "101-3", "No disp schedule", "No disp schedule", "week|1", true, null); + + testTX.commit(); + testTX = transactionService.getUserTransaction(); + testTX.begin(); + + recordFolder_1 = createRecordFolder(recordCategory_1, "F1", "101-3", "title", "description", "week|1", true); + recordFolder_2 = createRecordFolder(recordCategory_2, "F2", "102-3", "title", "description", "week|1", true); + recordFolder_3 = createRecordFolder(recordCategory_3, "F3", "103-3", "title", "description", "week|1", true); + record_1 = createRecord(recordFolder_1); + record_2 = createRecord(recordFolder_2); + record_3 = createRecord(recordFolder_3); + + // create people ... + + rm_user = "rm_user_" + storeRef.getIdentifier(); + rm_power_user = "rm_power_user_" + storeRef.getIdentifier(); + rm_security_officer = "rm_security_officer_" + storeRef.getIdentifier(); + rm_records_manager = "rm_records_manager_" + storeRef.getIdentifier(); + rm_administrator = "rm_administrator_" + storeRef.getIdentifier(); + + test_user = "test_user_" + storeRef.getIdentifier(); + + personService.createPerson(createDefaultProperties(rm_user)); + personService.createPerson(createDefaultProperties(rm_power_user)); + personService.createPerson(createDefaultProperties(rm_security_officer)); + personService.createPerson(createDefaultProperties(rm_records_manager)); + personService.createPerson(createDefaultProperties(rm_administrator)); + personService.createPerson(createDefaultProperties(test_user)); + + // create roles as groups + + rmUsers = authorityService.createAuthority(AuthorityType.GROUP, "RM_USER_" + storeRef.getIdentifier()); + rmPowerUsers = authorityService.createAuthority(AuthorityType.GROUP, "RM_POWER_USER_" + storeRef.getIdentifier()); + rmSecurityOfficers = authorityService.createAuthority(AuthorityType.GROUP, "RM_SECURITY_OFFICER_" + storeRef.getIdentifier()); + rmRecordsManagers = authorityService.createAuthority(AuthorityType.GROUP, "RM_RECORDS_MANAGER_" + storeRef.getIdentifier()); + rmAdministrators = authorityService.createAuthority(AuthorityType.GROUP, "RM_ADMINISTRATOR_" + storeRef.getIdentifier()); + testers = authorityService.createAuthority(AuthorityType.GROUP, "RM_TESTOR_" + storeRef.getIdentifier()); + + authorityService.addAuthority(testers, test_user); + + for (PermissionReference pr : permissionModel.getImmediateGranteePermissions(permissionModel.getPermissionReference(null, RMPermissionModel.ROLE_USER))) + { + setPermission(filePlan, rmUsers, pr.getName(), true); + } + authorityService.addAuthority(rmUsers, rm_user); + setPermission(filePlan, rm_user, RMPermissionModel.FILING, true); + + for (PermissionReference pr : permissionModel.getImmediateGranteePermissions(permissionModel.getPermissionReference(null, RMPermissionModel.ROLE_POWER_USER))) + { + setPermission(filePlan, rmPowerUsers, pr.getName(), true); + } + authorityService.addAuthority(rmPowerUsers, rm_power_user); + setPermission(filePlan, rm_power_user, RMPermissionModel.FILING, true); + + for (PermissionReference pr : permissionModel.getImmediateGranteePermissions(permissionModel.getPermissionReference(null, RMPermissionModel.ROLE_SECURITY_OFFICER))) + { + setPermission(filePlan, rmSecurityOfficers, pr.getName(), true); + } + authorityService.addAuthority(rmSecurityOfficers, rm_security_officer); + setPermission(filePlan, rm_security_officer, RMPermissionModel.FILING, true); + + for (PermissionReference pr : permissionModel.getImmediateGranteePermissions(permissionModel.getPermissionReference(null, RMPermissionModel.ROLE_RECORDS_MANAGER))) + { + setPermission(filePlan, rmRecordsManagers, pr.getName(), true); + } + authorityService.addAuthority(rmRecordsManagers, rm_records_manager); + setPermission(filePlan, rm_records_manager, RMPermissionModel.FILING, true); + + for (PermissionReference pr : permissionModel.getImmediateGranteePermissions(permissionModel.getPermissionReference(null, RMPermissionModel.ROLE_ADMINISTRATOR))) + { + setPermission(filePlan, rmAdministrators, pr.getName(), true); + } + authorityService.addAuthority(rmAdministrators, rm_administrator); + setPermission(filePlan, rm_administrator, RMPermissionModel.FILING, true); + + testTX.commit(); + testTX = transactionService.getUserTransaction(); + testTX.begin(); + } + + private void setPermission(NodeRef nodeRef, String authority, String permission, boolean allow) + { + permissionService.setPermission(nodeRef, authority, permission, allow); + if (permission.equals(RMPermissionModel.FILING)) + { + if (recordsManagementService.isRecordCategory(nodeRef) == true) + { + List assocs = nodeService.getChildAssocs(nodeRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef assoc : assocs) + { + NodeRef child = assoc.getChildRef(); + if (recordsManagementService.isRecordFolder(child) == true || recordsManagementService.isRecordCategory(child) == true) + { + setPermission(child, authority, permission, allow); + } + } + } + } + } + + private Map createDefaultProperties(String userName) + { + HashMap properties = new HashMap(); + properties.put(ContentModel.PROP_USERNAME, userName); + properties.put(ContentModel.PROP_HOMEFOLDER, null); + properties.put(ContentModel.PROP_FIRSTNAME, userName); + properties.put(ContentModel.PROP_LASTNAME, userName); + properties.put(ContentModel.PROP_EMAIL, userName); + properties.put(ContentModel.PROP_ORGID, ""); + return properties; + } + + private NodeRef createRecord(NodeRef recordFolder) + { + Map props = new HashMap(1); + props.put(ContentModel.PROP_NAME, "MyRecord.txt"); + NodeRef recordOne = this.nodeService.createNode(recordFolder, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "MyRecord.txt"), + ContentModel.TYPE_CONTENT, props).getChildRef(); + + // Set the content + ContentWriter writer = this.contentService.getWriter(recordOne, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent("There is some content in this record"); + return recordOne; + } + + private NodeRef createRecordSeries(NodeRef filePlan, String name, String identifier, String title, String description) + { + HashMap properties = new HashMap(); + properties.put(ContentModel.PROP_NAME, name); + properties.put(PROP_IDENTIFIER, identifier); + properties.put(ContentModel.PROP_TITLE, title); + properties.put(ContentModel.PROP_DESCRIPTION, description); + NodeRef answer = nodeService.createNode(filePlan, ContentModel.ASSOC_CONTAINS, TYPE_RECORD_CATEGORY, TYPE_RECORD_CATEGORY, properties).getChildRef(); + permissionService.setInheritParentPermissions(answer, false); + return answer; + } + + private NodeRef createRecordCategory(NodeRef recordSeries, String name, String identifier, String title, String description, String review, boolean vital, + Boolean recordLevelDisposition) + { + HashMap properties = new HashMap(); + properties.put(ContentModel.PROP_NAME, name); + properties.put(PROP_IDENTIFIER, identifier); + properties.put(ContentModel.PROP_TITLE, title); + properties.put(ContentModel.PROP_DESCRIPTION, description); + properties.put(PROP_REVIEW_PERIOD, review); + properties.put(PROP_VITAL_RECORD_INDICATOR, vital); + NodeRef answer = nodeService.createNode(recordSeries, ContentModel.ASSOC_CONTAINS, TYPE_RECORD_CATEGORY, TYPE_RECORD_CATEGORY, properties) + .getChildRef(); + + if (recordLevelDisposition != null) + { + properties = new HashMap(); + properties.put(PROP_DISPOSITION_AUTHORITY, "N1-218-00-4 item 023"); + properties.put(PROP_DISPOSITION_INSTRUCTIONS, "Cut off monthly, hold 1 month, then destroy."); + properties.put(PROP_RECORD_LEVEL_DISPOSITION, recordLevelDisposition); + NodeRef ds = nodeService.createNode(answer, ASSOC_DISPOSITION_SCHEDULE, TYPE_DISPOSITION_SCHEDULE, TYPE_DISPOSITION_SCHEDULE, + properties).getChildRef(); + + createDispoistionAction(ds, "cutoff", "monthend|1", null, "event"); + createDispoistionAction(ds, "transfer", "month|1", null, null); + createDispoistionAction(ds, "accession", "month|1", null, null); + createDispoistionAction(ds, "destroy", "month|1", "{http://www.alfresco.org/model/recordsmanagement/1.0}cutOffDate", null); + } + permissionService.setInheritParentPermissions(answer, false); + return answer; + } + + private NodeRef createDispoistionAction(NodeRef disposition, String actionName, String period, String periodProperty, String event) + { + HashMap properties = new HashMap(); + properties.put(PROP_DISPOSITION_ACTION_NAME, actionName); + properties.put(PROP_DISPOSITION_PERIOD, period); + if (periodProperty != null) + { + properties.put(PROP_DISPOSITION_PERIOD_PROPERTY, periodProperty); + } + if (event != null) + { + properties.put(PROP_DISPOSITION_EVENT, event); + } + NodeRef answer = nodeService.createNode(disposition, ASSOC_DISPOSITION_ACTION_DEFINITIONS, TYPE_DISPOSITION_ACTION_DEFINITION, + TYPE_DISPOSITION_ACTION_DEFINITION, properties).getChildRef(); + return answer; + } + + private NodeRef createRecordFolder(NodeRef recordCategory, String name, String identifier, String title, String description, String review, boolean vital) + { + HashMap properties = new HashMap(); + properties.put(ContentModel.PROP_NAME, name); + properties.put(PROP_IDENTIFIER, identifier); + properties.put(ContentModel.PROP_TITLE, title); + properties.put(ContentModel.PROP_DESCRIPTION, description); + properties.put(PROP_REVIEW_PERIOD, review); + properties.put(PROP_VITAL_RECORD_INDICATOR, vital); + NodeRef answer = nodeService.createNode(recordCategory, ContentModel.ASSOC_CONTAINS, TYPE_RECORD_FOLDER, TYPE_RECORD_FOLDER, properties) + .getChildRef(); + permissionService.setInheritParentPermissions(answer, false); + return answer; + } + + /* + * (non-Javadoc) + * + * @see junit.framework.TestCase#tearDown() + */ + protected void tearDown() throws Exception + { + if (testTX.getStatus() == Status.STATUS_ACTIVE) + { + testTX.rollback(); + } + else if (testTX.getStatus() == Status.STATUS_MARKED_ROLLBACK) + { + testTX.rollback(); + } + AuthenticationUtil.clearCurrentSecurityContext(); + super.tearDown(); + } + + public void testPermissionsModel() + { + Set exposed = permissionModel.getExposedPermissions(RecordsManagementModel.ASPECT_FILE_PLAN_COMPONENT); + assertEquals(6, exposed.size()); + assertTrue(exposed.contains(permissionModel.getPermissionReference(RecordsManagementModel.ASPECT_FILE_PLAN_COMPONENT, RMPermissionModel.ROLE_ADMINISTRATOR))); + + Set all = permissionModel.getAllPermissions(RecordsManagementModel.ASPECT_FILE_PLAN_COMPONENT); + assertEquals(58 /* capbilities */* 2 + 5 /* roles */+ (2 /* Read+File */* 2) + 1 /* Filing */, all.size()); + + checkGranting(RMPermissionModel.ACCESS_AUDIT, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.ADD_MODIFY_EVENT_DATES, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER, + RMPermissionModel.ROLE_SECURITY_OFFICER, RMPermissionModel.ROLE_POWER_USER); + checkGranting(RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.CLOSE_FOLDERS, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER, RMPermissionModel.ROLE_SECURITY_OFFICER, + RMPermissionModel.ROLE_POWER_USER); + checkGranting(RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER, + RMPermissionModel.ROLE_SECURITY_OFFICER); + checkGranting(RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER, + RMPermissionModel.ROLE_SECURITY_OFFICER, RMPermissionModel.ROLE_POWER_USER); + checkGranting(RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.CYCLE_VITAL_RECORDS, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER, RMPermissionModel.ROLE_SECURITY_OFFICER, + RMPermissionModel.ROLE_POWER_USER); + checkGranting(RMPermissionModel.DECLARE_AUDIT_AS_RECORD, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.DECLARE_RECORDS, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER, RMPermissionModel.ROLE_SECURITY_OFFICER, + RMPermissionModel.ROLE_POWER_USER, RMPermissionModel.ROLE_USER); + checkGranting(RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER, + RMPermissionModel.ROLE_SECURITY_OFFICER, RMPermissionModel.ROLE_POWER_USER); + checkGranting(RMPermissionModel.DELETE_AUDIT, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.DELETE_LINKS, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.DELETE_RECORDS, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.DESTROY_RECORDS, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.DISPLAY_RIGHTS_REPORT, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.EDIT_NON_RECORD_METADATA, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER, + RMPermissionModel.ROLE_SECURITY_OFFICER, RMPermissionModel.ROLE_POWER_USER); + checkGranting(RMPermissionModel.EDIT_RECORD_METADATA, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER, + RMPermissionModel.ROLE_SECURITY_OFFICER, RMPermissionModel.ROLE_POWER_USER); + checkGranting(RMPermissionModel.EDIT_SELECTION_LISTS, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.EXPORT_AUDIT, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + // File does not exists + // checkGranting(RMPermissionModel.FILE_RECORDS, RMPermissionModel.ROLE_ADMINISTRATOR, + // RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.MANAGE_ACCESS_CONTROLS, RMPermissionModel.ROLE_ADMINISTRATOR); + checkGranting(RMPermissionModel.MANAGE_ACCESS_RIGHTS, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.MAP_EMAIL_METADATA, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.MOVE_RECORDS, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.PASSWORD_CONTROL, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.PLANNING_REVIEW_CYCLES, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER, + RMPermissionModel.ROLE_SECURITY_OFFICER, RMPermissionModel.ROLE_POWER_USER); + checkGranting(RMPermissionModel.RE_OPEN_FOLDERS, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER, RMPermissionModel.ROLE_SECURITY_OFFICER, + RMPermissionModel.ROLE_POWER_USER); + checkGranting(RMPermissionModel.SELECT_AUDIT_METADATA, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.TRIGGER_AN_EVENT, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.UNDECLARE_RECORDS, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.UNFREEZE, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.UPDATE_CLASSIFICATION_DATES, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER, + RMPermissionModel.ROLE_SECURITY_OFFICER); + checkGranting(RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER, + RMPermissionModel.ROLE_SECURITY_OFFICER); + checkGranting(RMPermissionModel.UPDATE_TRIGGER_DATES, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + checkGranting(RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER, + RMPermissionModel.ROLE_SECURITY_OFFICER); + checkGranting(RMPermissionModel.VIEW_RECORDS, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER, RMPermissionModel.ROLE_SECURITY_OFFICER, + RMPermissionModel.ROLE_POWER_USER, RMPermissionModel.ROLE_USER); + checkGranting(RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, RMPermissionModel.ROLE_ADMINISTRATOR, RMPermissionModel.ROLE_RECORDS_MANAGER); + + } + + private void checkGranting(String permission, String... roles) + { + Set granting = permissionModel.getGrantingPermissions(permissionModel.getPermissionReference(RecordsManagementModel.ASPECT_FILE_PLAN_COMPONENT, + permission)); + Set test = new HashSet(); + test.addAll(granting); + Set nonRM = new HashSet(); + for (PermissionReference pr : granting) + { + if (!pr.getQName().equals(RecordsManagementModel.ASPECT_FILE_PLAN_COMPONENT)) + { + nonRM.add(pr); + } + } + test.removeAll(nonRM); + assertEquals(roles.length + 1, test.size()); + for (String role : roles) + { + assertTrue(test.contains(permissionModel.getPermissionReference(RecordsManagementModel.ASPECT_FILE_PLAN_COMPONENT, role))); + } + + } + + public void testConfig() + { + assertEquals(6, recordsManagementSecurityService.getProtectedAspects().size()); + assertEquals(13, recordsManagementSecurityService.getProtectedProperties().size()); + + // Test action wire up + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.ACCESS_AUDIT).getActionNames().size()); + assertEquals(2, recordsManagementSecurityService.getCapability(RMPermissionModel.ADD_MODIFY_EVENT_DATES).getActionNames().size()); + assertEquals(2, recordsManagementSecurityService.getCapability(RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES).getActionNames().size()); + assertEquals(2, recordsManagementSecurityService.getCapability(RMPermissionModel.AUTHORIZE_ALL_TRANSFERS).getActionNames().size()); + assertEquals(2, recordsManagementSecurityService.getCapability(RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.CHANGE_OR_DELETE_REFERENCES).getActionNames().size()); + assertEquals(1, recordsManagementSecurityService.getCapability(RMPermissionModel.CLOSE_FOLDERS).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS).getActionNames().size()); + assertEquals(1, recordsManagementSecurityService.getCapability(RMPermissionModel.CYCLE_VITAL_RECORDS).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.DECLARE_AUDIT_AS_RECORD).getActionNames().size()); + assertEquals(2, recordsManagementSecurityService.getCapability(RMPermissionModel.DECLARE_RECORDS).getActionNames().size()); + assertEquals(1, recordsManagementSecurityService.getCapability(RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.DELETE_AUDIT).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.DELETE_LINKS).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.DELETE_RECORDS).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.DESTROY_RECORDS).getActionNames().size()); + assertEquals(1, recordsManagementSecurityService.getCapability(RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.DISPLAY_RIGHTS_REPORT).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.EDIT_DECLARED_RECORD_METADATA).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.EDIT_NON_RECORD_METADATA).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.EDIT_RECORD_METADATA).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.EDIT_SELECTION_LISTS).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.EXPORT_AUDIT).getActionNames().size()); + assertEquals(1, recordsManagementSecurityService.getCapability(RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE).getActionNames().size()); + assertEquals(1, recordsManagementSecurityService.getCapability(RMPermissionModel.FILE_RECORDS).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.MANAGE_ACCESS_CONTROLS).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.MANAGE_ACCESS_RIGHTS).getActionNames().size()); + assertEquals(1, recordsManagementSecurityService.getCapability(RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.MAP_EMAIL_METADATA).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.MOVE_RECORDS).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.PASSWORD_CONTROL).getActionNames().size()); + assertEquals(1, recordsManagementSecurityService.getCapability(RMPermissionModel.PLANNING_REVIEW_CYCLES).getActionNames().size()); + assertEquals(1, recordsManagementSecurityService.getCapability(RMPermissionModel.RE_OPEN_FOLDERS).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.SELECT_AUDIT_METADATA).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.TRIGGER_AN_EVENT).getActionNames().size()); + assertEquals(1, recordsManagementSecurityService.getCapability(RMPermissionModel.UNDECLARE_RECORDS).getActionNames().size()); + assertEquals(2, recordsManagementSecurityService.getCapability(RMPermissionModel.UNFREEZE).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.UPDATE_CLASSIFICATION_DATES).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.UPDATE_TRIGGER_DATES).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS).getActionNames().size()); + assertEquals(0, recordsManagementSecurityService.getCapability(RMPermissionModel.VIEW_RECORDS).getActionNames().size()); + assertEquals(1, recordsManagementSecurityService.getCapability(RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE).getActionNames().size()); + + } + + public void testFilePlanAsSystem() + { + Map access = recordsManagementSecurityService.getCapabilities(filePlan); + assertEquals(65, access.size()); + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + + } + + public void testFilePlanAsAdmin() + { + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + Map access = recordsManagementSecurityService.getCapabilities(filePlan); + assertEquals(65, access.size()); + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + + } + + public void testFilePlanAsAdministrator() + { + AuthenticationUtil.setFullyAuthenticatedUser(rm_administrator); + Map access = recordsManagementSecurityService.getCapabilities(filePlan); + assertEquals(65, access.size()); + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + } + + public void testFilePlanAsRecordsManager() + { + Set permissions = permissionService.getAllSetPermissions(filePlan); + for (AccessPermission ap : permissions) + { + System.out.println(ap.getAuthority() + " -> " + ap.getPermission() + " (" + ap.getPosition() + ")"); + } + + AuthenticationUtil.setFullyAuthenticatedUser(rm_records_manager); + Map access = recordsManagementSecurityService.getCapabilities(filePlan); + assertEquals(65, access.size()); // 58 + File + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + + } + + public void testFilePlanAsSecurityOfficer() + { + AuthenticationUtil.setFullyAuthenticatedUser(rm_security_officer); + Map access = recordsManagementSecurityService.getCapabilities(filePlan); + assertEquals(65, access.size()); // 58 + File + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.DENIED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.DENIED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.DENIED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + } + + public void testFilePlanAsPowerUser() + { + AuthenticationUtil.setFullyAuthenticatedUser(rm_power_user); + Map access = recordsManagementSecurityService.getCapabilities(filePlan); + assertEquals(65, access.size()); // 58 + File + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.DENIED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.DENIED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.DENIED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + } + + public void testFilePlanAsUser() + { + AuthenticationUtil.setFullyAuthenticatedUser(rm_user); + Map access = recordsManagementSecurityService.getCapabilities(filePlan); + assertEquals(65, access.size()); // 58 + File + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.DENIED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.DENIED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.DENIED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + } + + public void testRecordSeriesAsSystem() + { + Map access = recordsManagementSecurityService.getCapabilities(recordSeries); + assertEquals(65, access.size()); + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + + } + + public void testRecordSeriesAsAdmin() + { + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + Map access = recordsManagementSecurityService.getCapabilities(recordSeries); + assertEquals(65, access.size()); + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + + } + + public void testRecordSeriesAsAdministrator() + { + AuthenticationUtil.setFullyAuthenticatedUser(rm_administrator); + Map access = recordsManagementSecurityService.getCapabilities(recordSeries); + assertEquals(65, access.size()); + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + } + + public void testRecordSeriesAsRecordsManager() + { + AuthenticationUtil.setFullyAuthenticatedUser(rm_records_manager); + permissionService.setPermission(recordSeries, rm_records_manager, RMPermissionModel.FILING, true); + Map access = recordsManagementSecurityService.getCapabilities(recordSeries); + assertEquals(65, access.size()); // 58 + File + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + + } + + public void testRecordSeriesAsSecurityOfficer() + { + AuthenticationUtil.setFullyAuthenticatedUser(rm_security_officer); + permissionService.setPermission(recordSeries, rm_security_officer, RMPermissionModel.FILING, true); + Map access = recordsManagementSecurityService.getCapabilities(recordSeries); + assertEquals(65, access.size()); // 58 + File + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.DENIED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.DENIED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.DENIED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + } + + public void testRecordSeriesAsPowerUser() + { + AuthenticationUtil.setFullyAuthenticatedUser(rm_power_user); + permissionService.setPermission(recordSeries, rm_power_user, RMPermissionModel.FILING, true); + Map access = recordsManagementSecurityService.getCapabilities(recordSeries); + assertEquals(65, access.size()); // 58 + File + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.DENIED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.DENIED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.DENIED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + } + + public void testRecordSeriesAsUser() + { + AuthenticationUtil.setFullyAuthenticatedUser(rm_user); + permissionService.setPermission(recordSeries, rm_user, RMPermissionModel.FILING, true); + Map access = recordsManagementSecurityService.getCapabilities(recordSeries); + assertEquals(65, access.size()); // 58 + File + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.DENIED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.DENIED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.DENIED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + } + + public void testRecordCategoryAsSystem() + { + Map access = recordsManagementSecurityService.getCapabilities(recordCategory_1); + assertEquals(65, access.size()); + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + + } + + public void testRecordCategoryAsAdmin() + { + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + Map access = recordsManagementSecurityService.getCapabilities(recordCategory_1); + assertEquals(65, access.size()); + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + + } + + public void testRecordCategoryAsAdministrator() + { + AuthenticationUtil.setFullyAuthenticatedUser(rm_administrator); + Map access = recordsManagementSecurityService.getCapabilities(recordCategory_1); + assertEquals(65, access.size()); + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + } + + public void testRecordCategoryAsRecordsManager() + { + AuthenticationUtil.setFullyAuthenticatedUser(rm_records_manager); + permissionService.setPermission(recordCategory_1, rm_records_manager, RMPermissionModel.FILING, true); + Map access = recordsManagementSecurityService.getCapabilities(recordCategory_1); + assertEquals(65, access.size()); // 58 + File + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + + } + + public void testRecordCategoryAsSecurityOfficer() + { + AuthenticationUtil.setFullyAuthenticatedUser(rm_security_officer); + permissionService.setPermission(recordCategory_1, rm_security_officer, RMPermissionModel.FILING, true); + Map access = recordsManagementSecurityService.getCapabilities(recordCategory_1); + assertEquals(65, access.size()); // 58 + File + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.DENIED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.DENIED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.DENIED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + } + + public void testRecordCategoryAsPowerUser() + { + AuthenticationUtil.setFullyAuthenticatedUser(rm_power_user); + permissionService.setPermission(recordCategory_1, rm_power_user, RMPermissionModel.FILING, true); + Map access = recordsManagementSecurityService.getCapabilities(recordCategory_1); + assertEquals(65, access.size()); // 58 + File + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.DENIED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.DENIED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.DENIED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + } + + public void testRecordCategoryAsUser() + { + AuthenticationUtil.setFullyAuthenticatedUser(rm_user); + permissionService.setPermission(recordCategory_1, rm_user, RMPermissionModel.FILING, true); + Map access = recordsManagementSecurityService.getCapabilities(recordCategory_1); + assertEquals(65, access.size()); // 58 + File + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.DENIED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.DENIED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.DENIED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + } + + public void testRecordFolderAsSystem() + { + Map access = recordsManagementSecurityService.getCapabilities(recordFolder_1); + assertEquals(65, access.size()); + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + + } + + public void testRecordFolderAsAdmin() + { + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + Map access = recordsManagementSecurityService.getCapabilities(recordFolder_1); + assertEquals(65, access.size()); + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + + } + + public void testRecordFolderAsAdministrator() + { + AuthenticationUtil.setFullyAuthenticatedUser(rm_administrator); + Map access = recordsManagementSecurityService.getCapabilities(recordFolder_1); + assertEquals(65, access.size()); + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + } + + private void setFilingOnRecordFolder(NodeRef recordFolder, String authority) + { + permissionService.setPermission(recordFolder, authority, RMPermissionModel.FILING, true); + permissionService.setPermission(nodeService.getPrimaryParent(recordFolder).getParentRef(), authority, RMPermissionModel.READ_RECORDS, true); + } + + public void testRecordFolderAsRecordsManager() + { + AuthenticationUtil.setFullyAuthenticatedUser(rm_records_manager); + setFilingOnRecordFolder(recordFolder_1, rm_records_manager); + Map access = recordsManagementSecurityService.getCapabilities(recordFolder_1); + assertEquals(65, access.size()); // 58 + File + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + + } + + public void testRecordFolderAsSecurityOfficer() + { + AuthenticationUtil.setFullyAuthenticatedUser(rm_security_officer); + permissionService.setPermission(recordFolder_1, rm_security_officer, RMPermissionModel.FILING, true); + permissionService.setPermission(nodeService.getPrimaryParent(recordFolder_1).getParentRef(), rm_security_officer, RMPermissionModel.READ_RECORDS, true); + Map access = recordsManagementSecurityService.getCapabilities(recordFolder_1); + assertEquals(65, access.size()); // 58 + File + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.DENIED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.DENIED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.DENIED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + } + + public void testRecordFolderAsPowerUser() + { + AuthenticationUtil.setFullyAuthenticatedUser(rm_power_user); + permissionService.setPermission(recordFolder_1, rm_power_user, RMPermissionModel.FILING, true); + permissionService.setPermission(nodeService.getPrimaryParent(recordFolder_1).getParentRef(), rm_power_user, RMPermissionModel.READ_RECORDS, true); + Map access = recordsManagementSecurityService.getCapabilities(recordFolder_1); + assertEquals(65, access.size()); // 58 + File + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.DENIED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.DENIED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.DENIED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + } + + public void testRecordFolderAsUser() + { + AuthenticationUtil.setFullyAuthenticatedUser(rm_user); + setFilingOnRecordFolder(recordFolder_1, rm_user); + Map access = recordsManagementSecurityService.getCapabilities(recordFolder_1); + assertEquals(65, access.size()); // 58 + File + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.DENIED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.DENIED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.DENIED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + } + + public void testRecordAsSystem() + { + Map access = recordsManagementSecurityService.getCapabilities(record_1); + assertEquals(65, access.size()); + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + + } + + public void testRecordAsAdmin() + { + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + Map access = recordsManagementSecurityService.getCapabilities(record_1); + assertEquals(65, access.size()); + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + + } + + public void testRecordAsAdministrator() + { + AuthenticationUtil.setFullyAuthenticatedUser(rm_administrator); + Map access = recordsManagementSecurityService.getCapabilities(record_1); + assertEquals(65, access.size()); + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + } + + public void testRecordAsRecordsManager() + { + AuthenticationUtil.setFullyAuthenticatedUser(rm_records_manager); + setFilingOnRecord(record_1, rm_records_manager); + Map access = recordsManagementSecurityService.getCapabilities(record_1); + assertEquals(65, access.size()); // 58 + File + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.ALLOWED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + + } + + public void testRecordAsSecurityOfficer() + { + AuthenticationUtil.setFullyAuthenticatedUser(rm_security_officer); + setFilingOnRecord(record_1, rm_security_officer); + Map access = recordsManagementSecurityService.getCapabilities(record_1); + assertEquals(65, access.size()); // 58 + File + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.DENIED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.DENIED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.DENIED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + } + + private void setFilingOnRecord(NodeRef record, String authority) + { + NodeRef recordFolder = nodeService.getPrimaryParent(record).getParentRef(); + permissionService.setPermission(recordFolder, authority, RMPermissionModel.FILING, true); + permissionService.setPermission(nodeService.getPrimaryParent(recordFolder).getParentRef(), authority, RMPermissionModel.READ_RECORDS, true); + } + + public void testRecordAsPowerUser() + { + AuthenticationUtil.setFullyAuthenticatedUser(rm_power_user); + setFilingOnRecord(record_1, rm_power_user); + Map access = recordsManagementSecurityService.getCapabilities(record_1); + assertEquals(65, access.size()); // 58 + File + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.DENIED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.DENIED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.ALLOWED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.DENIED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + } + + public void testRecordAsUser() + { + AuthenticationUtil.setFullyAuthenticatedUser(rm_user); + Map access = recordsManagementSecurityService.getCapabilities(record_1); + assertEquals(65, access.size()); // 58 + File + check(access, RMPermissionModel.ACCESS_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + check(access, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.DENIED); + check(access, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.DENIED); + check(access, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.DELETE_LINKS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + check(access, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.DENIED); + check(access, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.DENIED); + check(access, RMPermissionModel.EXPORT_AUDIT, AccessStatus.DENIED); + check(access, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + check(access, RMPermissionModel.MANUALLY_CHANGE_DISPOSITION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.MOVE_RECORDS, AccessStatus.UNDETERMINED); + check(access, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.DENIED); + check(access, RMPermissionModel.PLANNING_REVIEW_CYCLES, AccessStatus.DENIED); + check(access, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + check(access, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.DENIED); + check(access, RMPermissionModel.TRIGGER_AN_EVENT, AccessStatus.DENIED); + check(access, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_TRIGGER_DATES, AccessStatus.DENIED); + check(access, RMPermissionModel.UPDATE_VITAL_RECORD_CYCLE_INFORMATION, AccessStatus.DENIED); + check(access, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.DENIED); + check(access, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + check(access, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + } + + private void checkCapability(String user, NodeRef nodeRef, String permission, AccessStatus accessStstus) + { + AuthenticationUtil.setFullyAuthenticatedUser(user); + Map access = capabilityService.getCapabilitiesAccessState(nodeRef); + check(access, permission, accessStstus); + } + + private void checkPermission(String user, NodeRef nodeRef, String permission, AccessStatus accessStstus) + { + AuthenticationUtil.setFullyAuthenticatedUser(user); + assertTrue(permissionService.hasPermission(nodeRef, permission) == accessStstus); + } + + public void testAccessAuditCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.ACCESS_AUDIT, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.ACCESS_AUDIT, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.ACCESS_AUDIT, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.ACCESS_AUDIT, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.ACCESS_AUDIT, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.ACCESS_AUDIT, AccessStatus.DENIED); + } + + public void testAddModifyEventDatesCapability() + { + // Folder + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + checkPermission(rm_user, filePlan, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + + // folder level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(rm_administrator, record_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(rm_power_user, record_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(rm_user, record_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + checkCapability(rm_administrator, record_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + checkCapability(rm_power_user, record_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + checkCapability(rm_user, record_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + + // check person with no access and add read and write + // Filing + + checkCapability(test_user, recordFolder_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + permissionService.setInheritParentPermissions(recordCategory_1, false); + permissionService.setInheritParentPermissions(recordCategory_2, false); + permissionService.setPermission(recordCategory_1, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordCategory_2, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.ADD_MODIFY_EVENT_DATES, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + + permissionService.deletePermission(recordFolder_1, testers, RMPermissionModel.FILING); + permissionService.deletePermission(recordFolder_2, testers, RMPermissionModel.FILING); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + + // check frozen + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + Map params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "one"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "Two"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "unfreeze"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "unfreeze"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + + // Check closed + // should make no difference + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "closeRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "closeRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "openRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "openRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + + // try and complete some events + + AuthenticationUtil.setFullyAuthenticatedUser(test_user); + Map eventDetails = new HashMap(3); + eventDetails.put(CompleteEventAction.PARAM_EVENT_NAME, "event"); + eventDetails.put(CompleteEventAction.PARAM_EVENT_COMPLETED_AT, new Date()); + eventDetails.put(CompleteEventAction.PARAM_EVENT_COMPLETED_BY, test_user); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "completeEvent", eventDetails); + try + { + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "completeEvent", eventDetails); + fail(); + } + catch (AccessDeniedException ade) + { + + } + try + { + recordsManagementActionService.executeRecordsManagementAction(record_1, "completeEvent", eventDetails); + fail(); + } + catch (AccessDeniedException ade) + { + + } + + recordsManagementActionService.executeRecordsManagementAction(record_2, "completeEvent", eventDetails); + + // check protected properties + + try + { + publicNodeService.setProperty(record_1, RecordsManagementModel.PROP_EVENT_EXECUTION_COMPLETE, true); + fail(); + } + catch (AccessDeniedException ade) + { + + } + try + { + publicNodeService.setProperty(record_1, RecordsManagementModel.PROP_EVENT_EXECUTION_COMPLETED_AT, new Date()); + fail(); + } + catch (AccessDeniedException ade) + { + + } + try + { + publicNodeService.setProperty(record_1, RecordsManagementModel.PROP_EVENT_EXECUTION_COMPLETED_BY, "me"); + fail(); + } + catch (AccessDeniedException ade) + { + + } + + // check cutoff + + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + + nodeService.setProperty(record_1, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + nodeService.setProperty(record_1, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + nodeService.setProperty(record_1, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + nodeService.setProperty(record_1, ContentModel.PROP_TITLE, "titleValue"); + recordsManagementActionService.executeRecordsManagementAction(record_1, "declareRecord"); + + nodeService.setProperty(record_2, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + nodeService.setProperty(record_2, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + nodeService.setProperty(record_2, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + nodeService.setProperty(record_2, ContentModel.PROP_TITLE, "titleValue"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "declareRecord"); + + NodeRef ndNodeRef = this.nodeService.getChildAssocs(recordFolder_1, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + ndNodeRef = this.nodeService.getChildAssocs(record_2, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "cutoff", null); + recordsManagementActionService.executeRecordsManagementAction(record_2, "cutoff", null); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.ADD_MODIFY_EVENT_DATES, AccessStatus.ALLOWED); + } + + public void testApproveRecordsScheduledForCutoffCapability() + { + // Folder + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + + // folder level - not eligible all deny + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_administrator, record_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_power_user, record_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_user, record_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + + // record level - not eligible all deny + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_administrator, record_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_power_user, record_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_user, record_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + + // Set appropriate state - declare records and make eligible + + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + + nodeService.setProperty(record_1, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + nodeService.setProperty(record_1, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + nodeService.setProperty(record_1, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + nodeService.setProperty(record_1, ContentModel.PROP_TITLE, "titleValue"); + recordsManagementActionService.executeRecordsManagementAction(record_1, "declareRecord"); + + nodeService.setProperty(record_2, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + nodeService.setProperty(record_2, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + nodeService.setProperty(record_2, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + nodeService.setProperty(record_2, ContentModel.PROP_TITLE, "titleValue"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "declareRecord"); + + NodeRef ndNodeRef = this.nodeService.getChildAssocs(recordFolder_1, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + ndNodeRef = this.nodeService.getChildAssocs(record_2, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + + // folder level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.ALLOWED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_administrator, record_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_power_user, record_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_user, record_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.ALLOWED); + checkCapability(rm_administrator, record_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_power_user, record_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(rm_user, record_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + + // check person with no access and add read and write + // Filing + + checkCapability(test_user, recordFolder_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + permissionService.setInheritParentPermissions(recordCategory_1, false); + permissionService.setInheritParentPermissions(recordCategory_2, false); + permissionService.setPermission(recordCategory_1, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordCategory_2, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.ALLOWED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.ALLOWED); + + permissionService.deletePermission(recordFolder_1, testers, RMPermissionModel.FILING); + permissionService.deletePermission(recordFolder_2, testers, RMPermissionModel.FILING); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.ALLOWED); + + // check frozen + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + Map params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "one"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.ALLOWED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "Two"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "unfreeze"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "unfreeze"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.ALLOWED); + + // Check closed + // should make no difference + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "closeRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "closeRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.ALLOWED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "openRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "openRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, AccessStatus.ALLOWED); + + // try and cut off + + AuthenticationUtil.setFullyAuthenticatedUser(test_user); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "cutoff", null); + try + { + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "cutoff", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + try + { + recordsManagementActionService.executeRecordsManagementAction(record_1, "cutoff", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + recordsManagementActionService.executeRecordsManagementAction(record_2, "cutoff", null); + + // check protected properties + + try + { + publicNodeService.setProperty(record_1, RecordsManagementModel.PROP_CUT_OFF_DATE, new Date()); + fail(); + } + catch (AccessDeniedException ade) + { + + } + + // check cutoff again (it is already cut off) + + // try + // { + // recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "cutoff", null); + // fail(); + // } + // catch (AccessDeniedException ade) + // { + // + // } + // try + // { + // recordsManagementActionService.executeRecordsManagementAction(record_2, "cutoff", null); + // fail(); + // } + // catch (AccessDeniedException ade) + // { + // + // } + + // checkCapability(test_user, recordFolder_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + // AccessStatus.DENIED); + // checkCapability(test_user, record_1, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + // AccessStatus.DENIED); + // checkCapability(test_user, recordFolder_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + // AccessStatus.DENIED); + // checkCapability(test_user, record_2, RMPermissionModel.APPROVE_RECORDS_SCHEDULED_FOR_CUTOFF, + // AccessStatus.DENIED); + } + + public void testAttachRulesToMetadataPropertiesCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.ATTACH_RULES_TO_METADATA_PROPERTIES, AccessStatus.DENIED); + } + + private void setupForTransfer() + { + // Folder + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + + // folder level - not eligible all deny + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_administrator, record_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_user, record_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + + // record level - not eligible all deny + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_administrator, record_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_user, record_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + + // Set appropriate state - declare records and make eligible + + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + + nodeService.setProperty(record_1, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + nodeService.setProperty(record_1, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + nodeService.setProperty(record_1, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + nodeService.setProperty(record_1, ContentModel.PROP_TITLE, "titleValue"); + recordsManagementActionService.executeRecordsManagementAction(record_1, "declareRecord"); + + nodeService.setProperty(record_2, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + nodeService.setProperty(record_2, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + nodeService.setProperty(record_2, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + nodeService.setProperty(record_2, ContentModel.PROP_TITLE, "titleValue"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "declareRecord"); + + NodeRef ndNodeRef = this.nodeService.getChildAssocs(recordFolder_1, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + ndNodeRef = this.nodeService.getChildAssocs(record_2, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "cutoff", null); + recordsManagementActionService.executeRecordsManagementAction(record_2, "cutoff", null); + + ndNodeRef = this.nodeService.getChildAssocs(recordFolder_1, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + ndNodeRef = this.nodeService.getChildAssocs(record_2, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + + // folder level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_administrator, record_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_user, record_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, record_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_user, record_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + + // check person with no access and add read and write + // Filing + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + permissionService.setInheritParentPermissions(recordCategory_1, false); + permissionService.setInheritParentPermissions(recordCategory_2, false); + permissionService.setPermission(recordCategory_1, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordCategory_2, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + + permissionService.deletePermission(recordFolder_1, testers, RMPermissionModel.FILING); + permissionService.deletePermission(recordFolder_2, testers, RMPermissionModel.FILING); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + + // check frozen + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + Map params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "one"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "Two"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "unfreeze"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "unfreeze"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + + // Check closed + // should make no difference + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "closeRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "closeRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "openRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "openRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + } + + private void setupForTransferComplete() + { + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_ALL_TRANSFERS, AccessStatus.ALLOWED); + + // check each action + + TransferAction transfer = (TransferAction) ctx.getBean("transfer"); + assertFalse(transfer.isExecutable(recordFolder_1, null)); + assertFalse(transfer.isExecutable(record_1, null)); + assertFalse(transfer.isExecutable(recordFolder_2, null)); + assertFalse(transfer.isExecutable(record_2, null)); + + TransferCompleteAction transferComplete = (TransferCompleteAction) ctx.getBean("transferComplete"); + assertTrue(transferComplete.isExecutable(recordFolder_1, null)); + assertFalse(transferComplete.isExecutable(record_1, null)); + assertFalse(transferComplete.isExecutable(recordFolder_2, null)); + assertTrue(transferComplete.isExecutable(record_2, null)); + } + + public void testAuthorizeAllTransfersCapability() + { + setupForTransfer(); + + // try and transfer + + AuthenticationUtil.setFullyAuthenticatedUser(test_user); + + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "transfer", null); + + recordsManagementActionService.executeRecordsManagementAction(record_2, "transfer", null); + + setupForTransferComplete(); + + // try and complete the transfer + + AuthenticationUtil.setFullyAuthenticatedUser(test_user); + + recordsManagementActionService.executeRecordsManagementAction(getTransferObject(recordFolder_1), "transferComplete", null); + } + + public void testAuthorizeAllTransfersCapability_TransferNegative() + { + setupForTransfer(); + + // try and transfer + + AuthenticationUtil.setFullyAuthenticatedUser(test_user); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "transfer", null); + + recordsManagementActionService.executeRecordsManagementAction(record_2, "transfer", null); + + // -ve checks (ALF-2749) + // note: ideally, each -ve test should be run independently (if we want outer/setup txn to rollback) + + try + { + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "transfer", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + try + { + recordsManagementActionService.executeRecordsManagementAction(record_1, "transfer", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + + // check protected properties + + // PROP_DISPOSITION_ACTION_STARTED_AT + // PROP_DISPOSITION_ACTION_STARTED_BY + // PROP_DISPOSITION_ACTION_COMPLETED_AT + // PROP_DISPOSITION_ACTION_COMPLETED_BY + + try + { + publicNodeService.setProperty(record_1, RecordsManagementModel.PROP_DISPOSITION_ACTION_STARTED_AT, true); + fail(); + } + catch (AccessDeniedException ade) + { + + } + + try + { + publicNodeService.setProperty(record_1, RecordsManagementModel.PROP_DISPOSITION_ACTION_STARTED_BY, true); + fail(); + } + catch (AccessDeniedException ade) + { + + } + + try + { + publicNodeService.setProperty(record_1, RecordsManagementModel.PROP_DISPOSITION_ACTION_COMPLETED_AT, true); + fail(); + } + catch (AccessDeniedException ade) + { + + } + + try + { + publicNodeService.setProperty(record_1, RecordsManagementModel.PROP_DISPOSITION_ACTION_COMPLETED_BY, true); + fail(); + } + catch (AccessDeniedException ade) + { + + } + + // check cutoff again (it is already cut off) + + try + { + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "transfer", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + catch (AlfrescoRuntimeException are) + { + + } + try + { + recordsManagementActionService.executeRecordsManagementAction(record_2, "transfer", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + catch (AlfrescoRuntimeException are) + { + + } + } + + public void testAuthorizeAllTransfersCapability_TransferCompleteNegative() + { + setupForTransfer(); + + // try and transfer + + AuthenticationUtil.setFullyAuthenticatedUser(test_user); + + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "transfer", null); + + recordsManagementActionService.executeRecordsManagementAction(record_2, "transfer", null); + + setupForTransferComplete(); + + // try and complete the transfer + + AuthenticationUtil.setFullyAuthenticatedUser(test_user); + + recordsManagementActionService.executeRecordsManagementAction(getTransferObject(recordFolder_1), "transferComplete", null); + + // -ve checks (ALF-2749) + // note: ideally, each -ve test should be run independently (if we want outer/setup txn to rollback) + + try + { + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "transferComplete", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + try + { + recordsManagementActionService.executeRecordsManagementAction(record_1, "transferComplete", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + try + { + // will fail as this is in the same transafer which is now done. + recordsManagementActionService.executeRecordsManagementAction(getTransferObject(record_2), "transferComplete", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + + // try again - should fail + + try + { + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "transferComplete", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + try + { + recordsManagementActionService.executeRecordsManagementAction(record_2, "transferComplete", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + } + + + private NodeRef getTransferObject(NodeRef fp) + { + List assocs = this.nodeService.getParentAssocs(fp, RecordsManagementModel.ASSOC_TRANSFERRED, RegexQNamePattern.MATCH_ALL); + if (assocs.size() > 0) + { + return assocs.get(0).getParentRef(); + } + else + { + return fp; + } + } + + private void setupForAccession() + { + // Folder + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + + // folder level - not eligible all deny + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_administrator, record_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_user, record_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + + // record level - not eligible all deny + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_administrator, record_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_user, record_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + + // Set appropriate state - declare records and make eligible + + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + + nodeService.setProperty(record_1, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + nodeService.setProperty(record_1, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + nodeService.setProperty(record_1, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + nodeService.setProperty(record_1, ContentModel.PROP_TITLE, "titleValue"); + recordsManagementActionService.executeRecordsManagementAction(record_1, "declareRecord"); + + nodeService.setProperty(record_2, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + nodeService.setProperty(record_2, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + nodeService.setProperty(record_2, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + nodeService.setProperty(record_2, ContentModel.PROP_TITLE, "titleValue"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "declareRecord"); + + NodeRef ndNodeRef = this.nodeService.getChildAssocs(recordFolder_1, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + ndNodeRef = this.nodeService.getChildAssocs(record_2, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "cutoff", null); + recordsManagementActionService.executeRecordsManagementAction(record_2, "cutoff", null); + + ndNodeRef = this.nodeService.getChildAssocs(recordFolder_1, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + ndNodeRef = this.nodeService.getChildAssocs(record_2, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "transfer", null); + recordsManagementActionService.executeRecordsManagementAction(record_2, "transfer", null); + recordsManagementActionService.executeRecordsManagementAction(getTransferObject(recordFolder_1), "transferComplete", null); + + assertTrue(this.nodeService.exists(recordFolder_1)); + ndNodeRef = this.nodeService.getChildAssocs(recordFolder_1, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + assertTrue(this.nodeService.exists(recordFolder_1)); + ndNodeRef = this.nodeService.getChildAssocs(record_2, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + + // folder level + + assertTrue(this.nodeService.exists(recordFolder_1)); + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_administrator, record_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_user, record_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, record_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(rm_user, record_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + + // check person with no access and add read and write + // Filing + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + permissionService.setInheritParentPermissions(recordCategory_1, false); + permissionService.setInheritParentPermissions(recordCategory_2, false); + permissionService.setPermission(recordCategory_1, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordCategory_2, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + + permissionService.deletePermission(recordFolder_1, testers, RMPermissionModel.FILING); + permissionService.deletePermission(recordFolder_2, testers, RMPermissionModel.FILING); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + + // check frozen + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + Map params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "one"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "Two"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "unfreeze"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "unfreeze"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + + // Check closed + // should make no difference + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "closeRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "closeRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "openRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "openRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + } + + private void setupForAccessionComplete() + { + checkCapability(test_user, recordFolder_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.AUTHORIZE_NOMINATED_TRANSFERS, AccessStatus.ALLOWED); + + // check each action + + TransferAction transfer = (TransferAction) ctx.getBean("accession"); + assertFalse(transfer.isExecutable(recordFolder_1, null)); + assertFalse(transfer.isExecutable(record_1, null)); + assertFalse(transfer.isExecutable(recordFolder_2, null)); + assertFalse(transfer.isExecutable(record_2, null)); + + TransferCompleteAction transferComplete = (TransferCompleteAction) ctx.getBean("accessionComplete"); + assertTrue(transferComplete.isExecutable(recordFolder_1, null)); + assertFalse(transferComplete.isExecutable(record_1, null)); + assertFalse(transferComplete.isExecutable(recordFolder_2, null)); + assertTrue(transferComplete.isExecutable(record_2, null)); + } + + public void testAuthorizeNominatedTransfersCapability() + { + setupForAccession(); + + // try accession + + AuthenticationUtil.setFullyAuthenticatedUser(test_user); + + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "accession", null); + + recordsManagementActionService.executeRecordsManagementAction(record_2, "accession", null); + + setupForAccessionComplete(); + + // try and complete the transfer + + AuthenticationUtil.setFullyAuthenticatedUser(test_user); + recordsManagementActionService.executeRecordsManagementAction(getTransferObject(recordFolder_1), "accessionComplete", null); + } + + public void testAuthorizeNominatedTransfersCapability_AccessionNegative() + { + setupForAccession(); + + // try accession + + AuthenticationUtil.setFullyAuthenticatedUser(test_user); + + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "accession", null); + + recordsManagementActionService.executeRecordsManagementAction(record_2, "accession", null); + + // -ve checks (ALF-2749) + // note: ideally, each -ve test should be run independently (if we want outer/setup txn to rollback) + + try + { + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "accession", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + try + { + recordsManagementActionService.executeRecordsManagementAction(record_1, "accession", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + + // check protected properties + + // PROP_DISPOSITION_ACTION_STARTED_AT + // PROP_DISPOSITION_ACTION_STARTED_BY + // PROP_DISPOSITION_ACTION_COMPLETED_AT + // PROP_DISPOSITION_ACTION_COMPLETED_BY + + try + { + publicNodeService.setProperty(record_1, RecordsManagementModel.PROP_DISPOSITION_ACTION_STARTED_AT, true); + fail(); + } + catch (AccessDeniedException ade) + { + + } + + try + { + publicNodeService.setProperty(record_1, RecordsManagementModel.PROP_DISPOSITION_ACTION_STARTED_BY, true); + fail(); + } + catch (AccessDeniedException ade) + { + + } + + try + { + publicNodeService.setProperty(record_1, RecordsManagementModel.PROP_DISPOSITION_ACTION_COMPLETED_AT, true); + fail(); + } + catch (AccessDeniedException ade) + { + + } + + try + { + publicNodeService.setProperty(record_1, RecordsManagementModel.PROP_DISPOSITION_ACTION_COMPLETED_BY, true); + fail(); + } + catch (AccessDeniedException ade) + { + + } + + // check cutoff again (it is already cut off) + + try + { + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "accession", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + catch (AlfrescoRuntimeException are) + { + + } + try + { + recordsManagementActionService.executeRecordsManagementAction(record_2, "accession", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + catch (AlfrescoRuntimeException are) + { + + } + } + + public void testAuthorizeNominatedTransfersCapability_AccessionCompleteNegative() + { + setupForAccession(); + + // try accession + + AuthenticationUtil.setFullyAuthenticatedUser(test_user); + + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "accession", null); + + recordsManagementActionService.executeRecordsManagementAction(record_2, "accession", null); + + setupForAccessionComplete(); + + // try and complete the transfer + + AuthenticationUtil.setFullyAuthenticatedUser(test_user); + recordsManagementActionService.executeRecordsManagementAction(getTransferObject(recordFolder_1), "accessionComplete", null); + + // -ve checks (ALF-2749) + // note: ideally, each -ve test should be run independently (if we want outer/setup txn to rollback) + + try + { + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "accessionComplete", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + catch (AlfrescoRuntimeException are) + { + + } + try + { + recordsManagementActionService.executeRecordsManagementAction(record_1, "accessionComplete", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + catch (AlfrescoRuntimeException are) + { + + } + try + { + // will fail as this is in the same transfer which is now done. + recordsManagementActionService.executeRecordsManagementAction(getTransferObject(record_2), "accessionComplete", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + catch (AlfrescoRuntimeException are) + { + + } + + // try again - should fail + + try + { + recordsManagementActionService.executeRecordsManagementAction(getTransferObject(recordFolder_1), "accessionComplete", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + catch (AlfrescoRuntimeException are) + { + + } + try + { + recordsManagementActionService.executeRecordsManagementAction(getTransferObject(record_2), "accessionComplete", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + catch (AlfrescoRuntimeException are) + { + + } + } + + public void testChangeOrDeleteReferencesCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.CHANGE_OR_DELETE_REFERENCES, AccessStatus.DENIED); + } + + public void testCloseFoldersCapability() + { + // Folder + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkPermission(rm_user, filePlan, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + + // folder level - no preconditions + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_administrator, record_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_user, record_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + + // record level - record denies - folder allows + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_administrator, record_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_user, record_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + + // Set appropriate state - declare records and make eligible for cut off + + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + + nodeService.setProperty(record_1, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + nodeService.setProperty(record_1, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + nodeService.setProperty(record_1, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + nodeService.setProperty(record_1, ContentModel.PROP_TITLE, "titleValue"); + recordsManagementActionService.executeRecordsManagementAction(record_1, "declareRecord"); + + nodeService.setProperty(record_2, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + nodeService.setProperty(record_2, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + nodeService.setProperty(record_2, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + nodeService.setProperty(record_2, ContentModel.PROP_TITLE, "titleValue"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "declareRecord"); + + NodeRef ndNodeRef = this.nodeService.getChildAssocs(recordFolder_1, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + ndNodeRef = this.nodeService.getChildAssocs(record_2, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + + // folder level + + assertTrue(this.nodeService.exists(recordFolder_1)); + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_administrator, record_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_user, record_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_administrator, record_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_user, record_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + + // check person with no access and add read and write + // Filing + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + permissionService.setInheritParentPermissions(recordCategory_1, false); + permissionService.setInheritParentPermissions(recordCategory_2, false); + permissionService.setPermission(recordCategory_1, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordCategory_2, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.CLOSE_FOLDERS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + + permissionService.deletePermission(recordFolder_1, testers, RMPermissionModel.FILING); + permissionService.deletePermission(recordFolder_2, testers, RMPermissionModel.FILING); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + + // check frozen + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + Map params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "one"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "Two"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "Two"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "unfreeze"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "unfreeze"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "unfreeze"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + + // Check closed + // should make no difference + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "closeRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "closeRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "openRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "openRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CLOSE_FOLDERS, AccessStatus.DENIED); + + // try to close + + AuthenticationUtil.setFullyAuthenticatedUser(test_user); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "closeRecordFolder", null); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "closeRecordFolder", null); + + try + { + recordsManagementActionService.executeRecordsManagementAction(record_1, "closeRecordFolder", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + try + { + recordsManagementActionService.executeRecordsManagementAction(record_2, "closeRecordFolder", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + + // check protected properties + + // PROP_IS_CLOSED + + try + { + publicNodeService.setProperty(record_1, RecordsManagementModel.PROP_IS_CLOSED, true); + fail(); + } + catch (AccessDeniedException ade) + { + + } + + // check close again (it is already closed) + + AuthenticationUtil.setFullyAuthenticatedUser(test_user); + try + { + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "closeRecordFolder", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + try + { + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "closeRecordFolder", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + try + { + recordsManagementActionService.executeRecordsManagementAction(record_1, "closeRecordFolder", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + try + { + recordsManagementActionService.executeRecordsManagementAction(record_2, "closeRecordFolder", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + } + + public void testCreateAndAssociateSelectionListsCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.CREATE_AND_ASSOCIATE_SELECTION_LISTS, AccessStatus.DENIED); + } + + public void testCreateModifyDestroyClassificationGuidesCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.ALLOWED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_CLASSIFICATION_GUIDES, AccessStatus.DENIED); + } + + public void testCreateModifyDestroyEventsCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_EVENTS, AccessStatus.DENIED); + } + + public void testCreateModifyDestroyFileplanMetadataCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA, AccessStatus.DENIED); + } + + public void testCreateModifyDestroyFileplanTypesCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_TYPES, AccessStatus.DENIED); + } + + public void testCreateModifyDestroyFoldersCapability() + { + // Folder + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkPermission(rm_user, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + + // folder level - no preconditions + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_administrator, record_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_user, record_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_administrator, record_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_user, record_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + + // check person with no access and add read and write + // Filing + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + permissionService.setInheritParentPermissions(recordCategory_1, false); + permissionService.setInheritParentPermissions(recordCategory_2, false); + permissionService.setPermission(recordCategory_1, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordCategory_2, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + + permissionService.deletePermission(recordFolder_1, testers, RMPermissionModel.FILING); + permissionService.deletePermission(recordFolder_2, testers, RMPermissionModel.FILING); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + + // check frozen + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + Map params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "one"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "Two"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "Two"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "unfreeze"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "unfreeze"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "unfreeze"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + + // Check closed + // should make no difference + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "closeRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "closeRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "openRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "openRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + + // series level capabilities + + // fails as no filling rights ... + + checkCapability(test_user, recordCategory_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordCategory_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.DENIED); + + permissionService.setPermission(recordCategory_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordCategory_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordCategory_1, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, recordCategory_2, RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS, AccessStatus.ALLOWED); + + // create + + HashMap properties = new HashMap(); + properties.put(ContentModel.PROP_NAME, "name"); + properties.put(PROP_IDENTIFIER, "identifier"); + properties.put(ContentModel.PROP_TITLE, "title"); + properties.put(ContentModel.PROP_DESCRIPTION, "description"); + properties.put(PROP_REVIEW_PERIOD, "week|1"); + properties.put(PROP_VITAL_RECORD_INDICATOR, true); + NodeRef newFolder = publicNodeService.createNode(recordCategory_1, ContentModel.ASSOC_CONTAINS, TYPE_RECORD_FOLDER, TYPE_RECORD_FOLDER, + properties).getChildRef(); + + // modify + + publicNodeService.addAspect(newFolder, ContentModel.ASPECT_OWNABLE, null); + properties = new HashMap(); + properties.put(ContentModel.PROP_OWNER, "me"); + publicNodeService.addProperties(newFolder, properties); + // move should fail ... + try + { + publicNodeService.moveNode(newFolder, recordCategory_2, ContentModel.ASSOC_CONTAINS, TYPE_RECORD_FOLDER); + fail(); + } + catch (AccessDeniedException ade) + { + + } + publicNodeService.removeProperty(newFolder, ContentModel.PROP_TITLE); + publicNodeService.setProperty(newFolder, ContentModel.PROP_TITLE, "title"); + publicNodeService.addAspect(newFolder, ContentModel.ASPECT_TEMPORARY, null); + publicNodeService.removeAspect(newFolder, ContentModel.ASPECT_TEMPORARY); + publicNodeService.setProperties(newFolder, publicNodeService.getProperties(newFolder)); + try + { + // abstains + publicNodeService.setType(newFolder, TYPE_RECORD_FOLDER); + fail(); + } + catch (AccessDeniedException ade) + { + + } + + // try move + + permissionService.setPermission(filePlan, testers, RMPermissionModel.MOVE_RECORDS, true); + publicNodeService.moveNode(newFolder, recordCategory_2, ContentModel.ASSOC_CONTAINS, TYPE_RECORD_FOLDER); + + // delete + + publicNodeService.deleteNode(newFolder); + publicNodeService.deleteNode(recordFolder_1); + publicNodeService.deleteNode(recordFolder_2); + + } + + public void testCreateModifyDestroyRecordTypesCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_RECORD_TYPES, AccessStatus.DENIED); + } + + public void testCreateModifyDestroyReferenceTypesCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_REFERENCE_TYPES, AccessStatus.DENIED); + } + + public void testCreateModifyDestroyRolesCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_ROLES, AccessStatus.DENIED); + } + + public void testCreateModifyDestroyTimeframesCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_TIMEFRAMES, AccessStatus.DENIED); + } + + public void testCreateModifyDestroyUsersAndGroupsCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.CREATE_MODIFY_DESTROY_USERS_AND_GROUPS, AccessStatus.DENIED); + } + + public void testCreateModifyRecordsInCuttoffFoldersCapability() + { + // Folder + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + + // folder level - no preconditions + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, record_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_user, record_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, record_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_user, record_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + + // check person with no access and add read and write + // Filing + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + permissionService.setInheritParentPermissions(recordCategory_1, false); + permissionService.setInheritParentPermissions(recordCategory_2, false); + permissionService.setPermission(recordCategory_1, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordCategory_2, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + + permissionService.deletePermission(recordFolder_1, testers, RMPermissionModel.FILING); + permissionService.deletePermission(recordFolder_2, testers, RMPermissionModel.FILING); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + + // check frozen + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + Map params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "one"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "Two"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "Two"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "unfreeze"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "unfreeze"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "unfreeze"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + + // Check closed + // should make no difference + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "closeRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "closeRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "openRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "openRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + + // Check cutoff + // should make no difference + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + + nodeService.setProperty(record_1, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + nodeService.setProperty(record_1, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + nodeService.setProperty(record_1, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + nodeService.setProperty(record_1, ContentModel.PROP_TITLE, "titleValue"); + recordsManagementActionService.executeRecordsManagementAction(record_1, "declareRecord"); + + nodeService.setProperty(record_2, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + nodeService.setProperty(record_2, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + nodeService.setProperty(record_2, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + nodeService.setProperty(record_2, ContentModel.PROP_TITLE, "titleValue"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "declareRecord"); + + NodeRef ndNodeRef = this.nodeService.getChildAssocs(recordFolder_1, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + ndNodeRef = this.nodeService.getChildAssocs(record_2, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "cutoff"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "cutoff"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS, AccessStatus.ALLOWED); + + // create + + Map properties = new HashMap(1); + properties.put(ContentModel.PROP_NAME, "MyRecordCreate.txt"); + NodeRef newRecord = this.publicNodeService.createNode(recordFolder_1, ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "MyRecord.txt"), ContentModel.TYPE_CONTENT, properties).getChildRef(); + + // Set the content + ContentWriter writer = this.publicContentService.getWriter(newRecord, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent("There is some content in this record"); + + recordsManagementActionService.executeRecordsManagementAction(newRecord, "file"); + // modify + + publicNodeService.addAspect(newRecord, ContentModel.ASPECT_OWNABLE, null); + properties = new HashMap(); + properties.put(ContentModel.PROP_OWNER, "me"); + publicNodeService.addProperties(newRecord, properties); + // move should fail ... + try + { + publicNodeService.moveNode(newRecord, recordCategory_2, ContentModel.ASSOC_CONTAINS, TYPE_RECORD_FOLDER); + fail(); + } + catch (AccessDeniedException ade) + { + + } + publicNodeService.removeProperty(newRecord, ContentModel.PROP_TITLE); + publicNodeService.setProperty(newRecord, ContentModel.PROP_TITLE, "title"); + publicNodeService.addAspect(newRecord, ContentModel.ASPECT_TEMPORARY, null); + publicNodeService.removeAspect(newRecord, ContentModel.ASPECT_TEMPORARY); + publicNodeService.setProperties(newRecord, publicNodeService.getProperties(newRecord)); + try + { + // abstains + publicNodeService.setType(newRecord, TYPE_RECORD_FOLDER); + fail(); + } + catch (AccessDeniedException ade) + { + + } + + } + + public void testCycleVitalRecordsCapability() + { + // Folder + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_user, filePlan, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + + // folder level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, record_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_power_user, record_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_user, record_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, record_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_power_user, record_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_user, record_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + + // check person with no access and add read and write + // Filing + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + permissionService.setInheritParentPermissions(recordCategory_1, false); + permissionService.setInheritParentPermissions(recordCategory_2, false); + permissionService.setPermission(recordCategory_1, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordCategory_2, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.CYCLE_VITAL_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + + permissionService.deletePermission(recordFolder_1, testers, RMPermissionModel.FILING); + permissionService.deletePermission(recordFolder_2, testers, RMPermissionModel.FILING); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + + // check frozen + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + Map params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "one"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "Two"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "unfreeze"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "unfreeze"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + + // Check closed + // should make no difference + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "closeRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "closeRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "openRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "openRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + + // try and cycle + + recordsManagementActionService.executeRecordsManagementAction(record_1, "reviewed"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "reviewed"); + + recordsManagementActionService.executeRecordsManagementAction(record_1, "reviewed"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "reviewed"); + + // check cutoff + + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + + nodeService.setProperty(record_1, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + nodeService.setProperty(record_1, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + nodeService.setProperty(record_1, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + nodeService.setProperty(record_1, ContentModel.PROP_TITLE, "titleValue"); + recordsManagementActionService.executeRecordsManagementAction(record_1, "declareRecord"); + + nodeService.setProperty(record_2, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + nodeService.setProperty(record_2, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + nodeService.setProperty(record_2, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + nodeService.setProperty(record_2, ContentModel.PROP_TITLE, "titleValue"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "declareRecord"); + + NodeRef ndNodeRef = this.nodeService.getChildAssocs(recordFolder_1, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + ndNodeRef = this.nodeService.getChildAssocs(record_2, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "cutoff", null); + recordsManagementActionService.executeRecordsManagementAction(record_2, "cutoff", null); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.CYCLE_VITAL_RECORDS, AccessStatus.ALLOWED); + } + + public void testDeclareAuditAsRecordCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.DECLARE_AUDIT_AS_RECORD, AccessStatus.DENIED); + } + + public void testDeclareRecordsCapability() + { + // Folder + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_user, filePlan, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + + // folder level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_administrator, record_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_user, record_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_administrator, record_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_user, record_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + + // Set appropriate state - declare records and make eligible + + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + + nodeService.setProperty(record_1, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + nodeService.setProperty(record_1, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + nodeService.setProperty(record_1, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + nodeService.setProperty(record_1, ContentModel.PROP_TITLE, "titleValue"); + // recordsManagementActionService.executeRecordsManagementAction(record_1, "declareRecord"); + + nodeService.setProperty(record_2, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + nodeService.setProperty(record_2, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + nodeService.setProperty(record_2, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + nodeService.setProperty(record_2, ContentModel.PROP_TITLE, "titleValue"); + // recordsManagementActionService.executeRecordsManagementAction(record_2, "declareRecord"); + + // folder level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, record_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_power_user, record_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_user, record_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, record_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_power_user, record_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_user, record_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + + // check person with no access and add read and write + // Filing + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + permissionService.setInheritParentPermissions(recordCategory_1, false); + permissionService.setInheritParentPermissions(recordCategory_2, false); + permissionService.setPermission(recordCategory_1, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordCategory_2, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + + permissionService.deletePermission(recordFolder_1, testers, RMPermissionModel.FILING); + permissionService.deletePermission(recordFolder_2, testers, RMPermissionModel.FILING); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + + // check frozen + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + Map params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "one"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "Two"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "unfreeze"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "unfreeze"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + + // Check closed + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "closeRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "closeRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "openRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "openRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.ALLOWED); + + // try declare + + AuthenticationUtil.setFullyAuthenticatedUser(test_user); + try + { + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "declareRecord", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + recordsManagementActionService.executeRecordsManagementAction(record_1, "declareRecord"); + try + { + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "declareRecord", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + recordsManagementActionService.executeRecordsManagementAction(record_2, "declareRecord"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS, AccessStatus.DENIED); + } + + public void testDeclareRecordsInClosedFoldersCapability() + { + // Folder + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + checkPermission(rm_user, filePlan, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + + // folder level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_administrator, record_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_user, record_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_administrator, record_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_user, record_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + + // Set appropriate state - declare records and make eligible + + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + + nodeService.setProperty(record_1, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + nodeService.setProperty(record_1, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + nodeService.setProperty(record_1, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + nodeService.setProperty(record_1, ContentModel.PROP_TITLE, "titleValue"); + // recordsManagementActionService.executeRecordsManagementAction(record_1, "declareRecord"); + + nodeService.setProperty(record_2, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + nodeService.setProperty(record_2, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + nodeService.setProperty(record_2, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + nodeService.setProperty(record_2, ContentModel.PROP_TITLE, "titleValue"); + // recordsManagementActionService.executeRecordsManagementAction(record_2, "declareRecord"); + + // folder level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, record_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_power_user, record_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_user, record_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, record_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_power_user, record_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_user, record_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + + // check person with no access and add read and write + // Filing + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + permissionService.setInheritParentPermissions(recordCategory_1, false); + permissionService.setInheritParentPermissions(recordCategory_2, false); + permissionService.setPermission(recordCategory_1, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordCategory_2, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + + permissionService.deletePermission(recordFolder_1, testers, RMPermissionModel.FILING); + permissionService.deletePermission(recordFolder_2, testers, RMPermissionModel.FILING); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + + // check frozen + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + Map params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "one"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "Two"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "unfreeze"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "unfreeze"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + + // Check closed + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "closeRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "closeRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "openRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "openRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.ALLOWED); + + // try declare in closed + + // Close + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "closeRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "closeRecordFolder"); + + AuthenticationUtil.setFullyAuthenticatedUser(test_user); + try + { + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "declareRecord", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + recordsManagementActionService.executeRecordsManagementAction(record_1, "declareRecord"); + try + { + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "declareRecord", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + recordsManagementActionService.executeRecordsManagementAction(record_2, "declareRecord"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS, AccessStatus.DENIED); + } + + public void testDeleteAuditCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.DELETE_AUDIT, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.DELETE_AUDIT, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.DELETE_AUDIT, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.DELETE_AUDIT, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.DELETE_AUDIT, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.DELETE_AUDIT, AccessStatus.DENIED); + } + + public void testDeleteLinksCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.DELETE_LINKS, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.DELETE_LINKS, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.DELETE_LINKS, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.DELETE_LINKS, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.DELETE_LINKS, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.DELETE_LINKS, AccessStatus.DENIED); + } + + public void testDeleteRecordsCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.DELETE_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.DELETE_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.DELETE_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.DELETE_RECORDS, AccessStatus.DENIED); + } + + public void testDestroyRecordsCapability() + { + // Folder + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + + // folder level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(rm_administrator, record_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(rm_user, record_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, record_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(rm_user, record_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + + // Set appropriate state - declare records and make eligible + + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + + nodeService.setProperty(record_1, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + nodeService.setProperty(record_1, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + nodeService.setProperty(record_1, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + nodeService.setProperty(record_1, ContentModel.PROP_TITLE, "titleValue"); + recordsManagementActionService.executeRecordsManagementAction(record_1, "declareRecord"); + + nodeService.setProperty(record_2, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + nodeService.setProperty(record_2, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + nodeService.setProperty(record_2, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + nodeService.setProperty(record_2, ContentModel.PROP_TITLE, "titleValue"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "declareRecord"); + + NodeRef ndNodeRef = this.nodeService.getChildAssocs(recordFolder_1, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + ndNodeRef = this.nodeService.getChildAssocs(record_2, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + + // folder level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(rm_administrator, record_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(rm_user, record_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, record_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(rm_user, record_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + + // check person with no access and add read and write + // Filing + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + permissionService.setInheritParentPermissions(recordCategory_1, false); + permissionService.setInheritParentPermissions(recordCategory_2, false); + permissionService.setPermission(recordCategory_1, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordCategory_2, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.DESTROY_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + + permissionService.deletePermission(recordFolder_1, testers, RMPermissionModel.FILING); + permissionService.deletePermission(recordFolder_2, testers, RMPermissionModel.FILING); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + + // check frozen + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + Map params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "one"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "Two"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "unfreeze"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "unfreeze"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + + // Check closed + // should make no difference + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "closeRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "closeRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "openRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "openRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS, AccessStatus.ALLOWED); + + // cut off + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "cutoff", null); + recordsManagementActionService.executeRecordsManagementAction(record_2, "cutoff", null); + + // fix disposition + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + ndNodeRef = this.nodeService.getChildAssocs(recordFolder_1, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + ndNodeRef = this.nodeService.getChildAssocs(record_2, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + + // should delete even though transfer is next ..,. + + AuthenticationUtil.setFullyAuthenticatedUser(test_user); + nodeService.deleteNode(recordFolder_1); + nodeService.deleteNode(record_2); + + } + + public void testDestroyRecordsScheduledForDestructionCapability() + { + // Folder + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + + // folder level - not eligible all deny + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_administrator, record_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_power_user, record_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_user, record_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + + // record level - not eligible all deny + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_administrator, record_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_power_user, record_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_user, record_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + + // Set appropriate state - declare records and make eligible + + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + + nodeService.setProperty(record_1, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + nodeService.setProperty(record_1, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + nodeService.setProperty(record_1, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + nodeService.setProperty(record_1, ContentModel.PROP_TITLE, "titleValue"); + recordsManagementActionService.executeRecordsManagementAction(record_1, "declareRecord"); + + nodeService.setProperty(record_2, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + nodeService.setProperty(record_2, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + nodeService.setProperty(record_2, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + nodeService.setProperty(record_2, ContentModel.PROP_TITLE, "titleValue"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "declareRecord"); + + NodeRef ndNodeRef = this.nodeService.getChildAssocs(recordFolder_1, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + ndNodeRef = this.nodeService.getChildAssocs(record_2, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "cutoff", null); + recordsManagementActionService.executeRecordsManagementAction(record_2, "cutoff", null); + + ndNodeRef = this.nodeService.getChildAssocs(recordFolder_1, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + ndNodeRef = this.nodeService.getChildAssocs(record_2, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "transfer", null); + recordsManagementActionService.executeRecordsManagementAction(record_2, "transfer", null); + // this completes both transfers :-) + recordsManagementActionService.executeRecordsManagementAction(getTransferObject(recordFolder_1), "transferComplete", null); + + ndNodeRef = this.nodeService.getChildAssocs(recordFolder_1, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + ndNodeRef = this.nodeService.getChildAssocs(record_2, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "accession", null); + recordsManagementActionService.executeRecordsManagementAction(record_2, "accession", null); + + ndNodeRef = this.nodeService.getChildAssocs(recordFolder_1, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + ndNodeRef = this.nodeService.getChildAssocs(record_2, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + + // this completes both transfers :-) + recordsManagementActionService.executeRecordsManagementAction(getTransferObject(recordFolder_1), "transferComplete", null); + + ndNodeRef = this.nodeService.getChildAssocs(recordFolder_1, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + ndNodeRef = this.nodeService.getChildAssocs(record_2, RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + this.nodeService.setProperty(ndNodeRef, RecordsManagementModel.PROP_DISPOSITION_AS_OF, calendar.getTime()); + + // folder level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.ALLOWED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_administrator, record_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_power_user, record_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_user, record_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.ALLOWED); + checkCapability(rm_administrator, record_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_power_user, record_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(rm_user, record_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + + // check person with no access and add read and write + // Filing + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + permissionService.setInheritParentPermissions(recordCategory_1, false); + permissionService.setInheritParentPermissions(recordCategory_2, false); + permissionService.setPermission(recordCategory_1, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordCategory_2, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.ALLOWED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.DECLARE_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.ALLOWED); + + permissionService.deletePermission(recordFolder_1, testers, RMPermissionModel.FILING); + permissionService.deletePermission(recordFolder_2, testers, RMPermissionModel.FILING); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.ALLOWED); + + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.ALLOWED); + + // check frozen + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + Map params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "one"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.ALLOWED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "Two"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "unfreeze"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "unfreeze"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.ALLOWED); + + // Check closed + // should make no difference + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "closeRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "closeRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.ALLOWED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "openRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "openRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.DESTROY_RECORDS_SCHEDULED_FOR_DESTRUCTION, AccessStatus.ALLOWED); + + // scheduled destroy + + AuthenticationUtil.setFullyAuthenticatedUser(test_user); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "destroy", null); + recordsManagementActionService.executeRecordsManagementAction(record_2, "destroy", null); + + } + + public void testDisplayRightsReportCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.DISPLAY_RIGHTS_REPORT, AccessStatus.DENIED); + } + + public void testEditDeclaredRecordMetadataCapability() + { + // Folder + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + + // folder level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_administrator, record_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_power_user, record_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_user, record_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_administrator, record_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_power_user, record_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_user, record_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + + // Set appropriate state - declare records and make eligible + + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + + nodeService.setProperty(record_1, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + nodeService.setProperty(record_1, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + nodeService.setProperty(record_1, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + nodeService.setProperty(record_1, ContentModel.PROP_TITLE, "titleValue"); + recordsManagementActionService.executeRecordsManagementAction(record_1, "declareRecord"); + + nodeService.setProperty(record_2, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + nodeService.setProperty(record_2, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + nodeService.setProperty(record_2, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + nodeService.setProperty(record_2, ContentModel.PROP_TITLE, "titleValue"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "declareRecord"); + + // folder level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(rm_administrator, record_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_power_user, record_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_user, record_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(rm_administrator, record_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_power_user, record_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_user, record_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + + // check person with no access and add read and write + // Filing + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + permissionService.setInheritParentPermissions(recordCategory_1, false); + permissionService.setInheritParentPermissions(recordCategory_2, false); + permissionService.setPermission(recordCategory_1, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordCategory_2, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.ALLOWED); + + permissionService.deletePermission(recordFolder_1, testers, RMPermissionModel.FILING); + permissionService.deletePermission(recordFolder_2, testers, RMPermissionModel.FILING); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.ALLOWED); + + // check frozen + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + Map params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "one"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.ALLOWED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "Two"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "unfreeze"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "unfreeze"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.ALLOWED); + + // Check closed + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "closeRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "closeRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "openRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "openRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA, AccessStatus.ALLOWED); + + // try to modify + + publicNodeService.addAspect(record_1, ContentModel.ASPECT_OWNABLE, null); + Map properties = new HashMap(1); + properties.put(ContentModel.PROP_OWNER, "me"); + publicNodeService.addProperties(record_1, properties); + // move should fail ... + try + { + publicNodeService.moveNode(record_1, recordCategory_2, ContentModel.ASSOC_CONTAINS, TYPE_RECORD_FOLDER); + fail(); + } + catch (AccessDeniedException ade) + { + + } + publicNodeService.removeProperty(record_1, ContentModel.PROP_TITLE); + publicNodeService.setProperty(record_1, ContentModel.PROP_TITLE, "title"); + publicNodeService.addAspect(record_1, ContentModel.ASPECT_TEMPORARY, null); + publicNodeService.removeAspect(record_1, ContentModel.ASPECT_TEMPORARY); + publicNodeService.setProperties(record_1, publicNodeService.getProperties(record_1)); + try + { + // abstains + publicNodeService.setType(record_1, TYPE_RECORD_FOLDER); + fail(); + } + catch (AccessDeniedException ade) + { + + } + } + + public void testEditNonRecordMetadataCapability() + { + // Folder + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkPermission(rm_user, filePlan, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + + // folder level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_administrator, record_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_power_user, record_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_user, record_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_administrator, record_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_power_user, record_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_user, record_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + + // check person with no access and add read and write + // Filing + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + permissionService.setInheritParentPermissions(recordCategory_1, false); + permissionService.setInheritParentPermissions(recordCategory_2, false); + permissionService.setPermission(recordCategory_1, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordCategory_2, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.EDIT_NON_RECORD_METADATA, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.EDIT_NON_RECORD_METADATA); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.EDIT_NON_RECORD_METADATA, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + + permissionService.deletePermission(recordFolder_1, testers, RMPermissionModel.FILING); + permissionService.deletePermission(recordFolder_2, testers, RMPermissionModel.FILING); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + + // check frozen + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + Map params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "one"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "Two"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "unfreeze"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "unfreeze"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + + // Check closed + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "closeRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "closeRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "openRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "openRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_NON_RECORD_METADATA, AccessStatus.DENIED); + + // try to modify + + publicNodeService.addAspect(recordFolder_1, ContentModel.ASPECT_OWNABLE, null); + Map properties = new HashMap(1); + properties.put(ContentModel.PROP_OWNER, "me"); + publicNodeService.addProperties(recordFolder_1, properties); + // move should fail ... + try + { + publicNodeService.moveNode(recordFolder_1, recordCategory_2, ContentModel.ASSOC_CONTAINS, TYPE_RECORD_FOLDER); + fail(); + } + catch (AccessDeniedException ade) + { + + } + publicNodeService.removeProperty(recordFolder_1, ContentModel.PROP_TITLE); + publicNodeService.setProperty(recordFolder_1, ContentModel.PROP_TITLE, "title"); + publicNodeService.addAspect(recordFolder_1, ContentModel.ASPECT_TEMPORARY, null); + publicNodeService.removeAspect(recordFolder_1, ContentModel.ASPECT_TEMPORARY); + publicNodeService.setProperties(recordFolder_1, publicNodeService.getProperties(recordFolder_1)); + try + { + // abstains + publicNodeService.setType(recordFolder_1, TYPE_RECORD_FOLDER); + fail(); + } + catch (AccessDeniedException ade) + { + + } + } + + public void testEditRecordMetadataCapability() + { + // Folder + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + checkPermission(rm_user, filePlan, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + + // folder level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(rm_administrator, record_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(rm_power_user, record_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(rm_user, record_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(rm_administrator, record_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(rm_power_user, record_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(rm_user, record_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + + // check person with no access and add read and write + // Filing + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + permissionService.setInheritParentPermissions(recordCategory_1, false); + permissionService.setInheritParentPermissions(recordCategory_2, false); + permissionService.setPermission(recordCategory_1, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordCategory_2, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.EDIT_RECORD_METADATA, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.EDIT_RECORD_METADATA); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.EDIT_RECORD_METADATA, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + + permissionService.deletePermission(recordFolder_1, testers, RMPermissionModel.FILING); + permissionService.deletePermission(recordFolder_2, testers, RMPermissionModel.FILING); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + + // check frozen + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + Map params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "one"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "Two"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "unfreeze"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "unfreeze"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + + // Check closed + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "closeRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "closeRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "openRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "openRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EDIT_RECORD_METADATA, AccessStatus.ALLOWED); + + // try to modify + + publicNodeService.addAspect(record_1, ContentModel.ASPECT_OWNABLE, null); + Map properties = new HashMap(1); + properties.put(ContentModel.PROP_OWNER, "me"); + publicNodeService.addProperties(record_1, properties); + // move should fail ... + try + { + publicNodeService.moveNode(record_1, recordCategory_2, ContentModel.ASSOC_CONTAINS, TYPE_RECORD_FOLDER); + fail(); + } + catch (AccessDeniedException ade) + { + + } + publicNodeService.removeProperty(record_1, ContentModel.PROP_TITLE); + publicNodeService.setProperty(record_1, ContentModel.PROP_TITLE, "title"); + publicNodeService.addAspect(record_1, ContentModel.ASPECT_TEMPORARY, null); + publicNodeService.removeAspect(record_1, ContentModel.ASPECT_TEMPORARY); + publicNodeService.setProperties(record_1, publicNodeService.getProperties(record_1)); + try + { + // abstains + publicNodeService.setType(record_1, TYPE_RECORD_FOLDER); + fail(); + } + catch (AccessDeniedException ade) + { + + } + } + + public void testEditSelectionListsCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.EDIT_SELECTION_LISTS, AccessStatus.DENIED); + } + + public void testEnableDisableAuditByTypesCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.ENABLE_DISABLE_AUDIT_BY_TYPES, AccessStatus.DENIED); + } + + public void testExportAuditCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.EXPORT_AUDIT, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.EXPORT_AUDIT, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.EXPORT_AUDIT, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.EXPORT_AUDIT, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.EXPORT_AUDIT, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.EXPORT_AUDIT, AccessStatus.DENIED); + } + + public void testExtendRetentionPeriodOrFreezeCapability() + { + // freeze and unfreeze is part of most other tests - this jusr duplicates the basics ... + + // Folder + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + + // folder level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(rm_administrator, record_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_power_user, record_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_user, record_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(rm_administrator, record_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_power_user, record_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_user, record_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + + // check person with no access and add read and write + // Filing + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + permissionService.setInheritParentPermissions(recordCategory_1, false); + permissionService.setInheritParentPermissions(recordCategory_2, false); + permissionService.setPermission(recordCategory_1, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordCategory_2, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + + permissionService.deletePermission(recordFolder_1, testers, RMPermissionModel.FILING); + permissionService.deletePermission(recordFolder_2, testers, RMPermissionModel.FILING); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.DENIED); + + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + + // check frozen - can be in mutiple holds/freezes .. + + Map params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "one"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + + params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "Two"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "unfreeze"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "unfreeze"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + + // Check closed + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "closeRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "closeRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "openRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "openRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.EXTEND_RETENTION_PERIOD_OR_FREEZE, AccessStatus.ALLOWED); + + } + + public void testFileRecordsCapability() + { + // Folder + checkPermission(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_administrator, recordFolder_1, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, recordFolder_1, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, recordFolder_1, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_power_user, recordFolder_1, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_user, recordFolder_1, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + + // Record + checkPermission(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_administrator, record_1, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, record_1, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, record_1, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_power_user, record_1, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_user, record_1, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + + // folder level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, record_1, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_power_user, record_1, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_user, record_1, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, record_2, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_power_user, record_2, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_user, record_2, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + + // check person with no access and add read and write + // Filing + + checkCapability(test_user, recordFolder_1, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + permissionService.setInheritParentPermissions(recordCategory_1, false); + permissionService.setInheritParentPermissions(recordCategory_2, false); + permissionService.setPermission(recordCategory_1, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordCategory_2, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.FILE_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.FILE_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.FILE_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + + permissionService.deletePermission(recordFolder_1, testers, RMPermissionModel.FILING); + permissionService.deletePermission(recordFolder_2, testers, RMPermissionModel.FILING); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + + // check frozen + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + Map params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "one"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "Two"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "unfreeze"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "unfreeze"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + + // Check closed + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "closeRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "closeRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "openRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "openRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.FILE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.FILE_RECORDS, AccessStatus.ALLOWED); + + // Do some filing ... + + // create + + Map properties = new HashMap(1); + properties.put(ContentModel.PROP_NAME, "MyRecordCreate.txt"); + NodeRef newRecord_1 = this.publicNodeService.createNode(recordFolder_1, ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "MyRecord.txt"), ContentModel.TYPE_CONTENT, properties).getChildRef(); + + // Set the content (relies on owner in the DM side until it becode RM ified ...) + ContentWriter writer = this.publicContentService.getWriter(newRecord_1, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent("There is some content in this record"); + + assertFalse(recordsManagementService.isFilePlanComponent(newRecord_1)); + recordsManagementActionService.executeRecordsManagementAction(newRecord_1, "file"); + assertTrue(recordsManagementService.isFilePlanComponent(newRecord_1)); + + properties = new HashMap(1); + properties.put(ContentModel.PROP_NAME, "MyRecordCreate.txt"); + NodeRef newRecord_2 = this.publicNodeService.createNode(recordFolder_2, ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "MyRecord.txt"), ContentModel.TYPE_CONTENT, properties).getChildRef(); + + // Set the content + writer = this.publicContentService.getWriter(newRecord_2, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent("There is some content in this record"); + + recordsManagementActionService.executeRecordsManagementAction(newRecord_2, "file"); + + // update with permissions in place ... + + writer = this.publicContentService.getWriter(newRecord_1, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent("There is some updated content in this record"); + + writer = this.publicContentService.getWriter(newRecord_2, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent("There is some content in this record"); + } + + public void testMakeOptionalPropertiesMandatoryCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.MAKE_OPTIONAL_PARAMETERS_MANDATORY, AccessStatus.DENIED); + } + + public void testManageAccessControlsCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.DENIED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.MANAGE_ACCESS_CONTROLS, AccessStatus.DENIED); + } + + public void testManageAccessRightsCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.MANAGE_ACCESS_RIGHTS, AccessStatus.DENIED); + } + + public void testManuallyChangeDispositionDatesCapability() + { + // TODO: The action is not yet done + } + + public void testMapClassificationGuideMetadataCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.MAP_CLASSIFICATION_GUIDE_METADATA, AccessStatus.DENIED); + } + + public void testMapEmailMetadataCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.MAP_EMAIL_METADATA, AccessStatus.DENIED); + } + + public void testMoveRecordsCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.MOVE_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.MOVE_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.MOVE_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.MOVE_RECORDS, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.MOVE_RECORDS, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.MOVE_RECORDS, AccessStatus.DENIED); + } + + public void testPasswordControlCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.PASSWORD_CONTROL, AccessStatus.DENIED); + } + + public void testPlanningReviewCyclesCapability() + { + // TODO: Waiting for the appropriate action + } + + public void testReOpenFoldersCapability() + { + // Folder + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkPermission(rm_user, filePlan, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + + // folder level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_administrator, record_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_user, record_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_administrator, record_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_user, record_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + + // Check closed + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "closeRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "closeRecordFolder"); + + // folder level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_administrator, record_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_user, record_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_administrator, record_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(rm_user, record_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + + // check person with no access and add read and write + // Filing + + checkCapability(test_user, recordFolder_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + permissionService.setInheritParentPermissions(recordCategory_1, false); + permissionService.setInheritParentPermissions(recordCategory_2, false); + permissionService.setPermission(recordCategory_1, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordCategory_2, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.RE_OPEN_FOLDERS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.RE_OPEN_FOLDERS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.RE_OPEN_FOLDERS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + + permissionService.deletePermission(recordFolder_1, testers, RMPermissionModel.FILING); + permissionService.deletePermission(recordFolder_2, testers, RMPermissionModel.FILING); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + + // check frozen + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + Map params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "one"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "Two"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "unfreeze"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "unfreeze"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "openRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "openRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.ALLOWED); + checkCapability(test_user, record_2, RMPermissionModel.RE_OPEN_FOLDERS, AccessStatus.DENIED); + + } + + public void testSelectAuditMetadataCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.SELECT_AUDIT_METADATA, AccessStatus.DENIED); + } + + public void testTriggerAnEventCapability() + { + // TODO: Waiting for action + } + + public void testUndeclareRecordsCapability() + { + // Folder + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + + // folder level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_administrator, record_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_user, record_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_administrator, record_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_user, record_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + + // Set appropriate state - declare records and make eligible + + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + + nodeService.setProperty(record_1, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + nodeService.setProperty(record_1, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + nodeService.setProperty(record_1, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + nodeService.setProperty(record_1, ContentModel.PROP_TITLE, "titleValue"); + recordsManagementActionService.executeRecordsManagementAction(record_1, "declareRecord"); + + nodeService.setProperty(record_2, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + nodeService.setProperty(record_2, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + nodeService.setProperty(record_2, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + nodeService.setProperty(record_2, ContentModel.PROP_TITLE, "titleValue"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "declareRecord"); + + // folder level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, record_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_user, record_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_administrator, record_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_power_user, record_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(rm_user, record_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + + // check person with no access and add read and write + // Filing + + checkCapability(test_user, recordFolder_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + permissionService.setInheritParentPermissions(recordCategory_1, false); + permissionService.setInheritParentPermissions(recordCategory_2, false); + permissionService.setPermission(recordCategory_1, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordCategory_2, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.UNDECLARE_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.UNDECLARE_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.UNDECLARE_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.ALLOWED); + + permissionService.deletePermission(recordFolder_1, testers, RMPermissionModel.FILING); + permissionService.deletePermission(recordFolder_2, testers, RMPermissionModel.FILING); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.ALLOWED); + + // check frozen + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + Map params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "one"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.ALLOWED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "Two"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "freeze", params); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "unfreeze"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "unfreeze"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.ALLOWED); + + // Check closed + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "closeRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "closeRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.ALLOWED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "openRecordFolder"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "openRecordFolder"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.ALLOWED); + + // try undeclare + + AuthenticationUtil.setFullyAuthenticatedUser(test_user); + try + { + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "undeclareRecord", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + recordsManagementActionService.executeRecordsManagementAction(record_1, "undeclareRecord"); + try + { + recordsManagementActionService.executeRecordsManagementAction(recordFolder_2, "undeclareRecord", null); + fail(); + } + catch (AccessDeniedException ade) + { + + } + recordsManagementActionService.executeRecordsManagementAction(record_2, "undeclareRecord"); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.UNDECLARE_RECORDS, AccessStatus.DENIED); + } + + public void testUnfreezeCapability() + { + // freeze and unfreeze is part of most other tests - this jusr duplicates the basics ... + + // Folder + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.UNFREEZE, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.UNFREEZE, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.UNFREEZE, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + + // folder level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_administrator, record_1, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_power_user, record_1, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_user, record_1, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_administrator, record_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_power_user, record_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_user, record_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + Map params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "one"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "freeze", params); + params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "Two"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "freeze", params); + + // folder level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.UNFREEZE, AccessStatus.ALLOWED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.UNFREEZE, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.UNFREEZE, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.UNFREEZE, AccessStatus.ALLOWED); + checkCapability(rm_administrator, record_1, RMPermissionModel.UNFREEZE, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.UNFREEZE, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_power_user, record_1, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_user, record_1, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.UNFREEZE, AccessStatus.ALLOWED); + checkCapability(rm_administrator, record_2, RMPermissionModel.UNFREEZE, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.UNFREEZE, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_power_user, record_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(rm_user, record_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + + // check person with no access and add read and write + // Filing + + checkCapability(test_user, recordFolder_1, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + permissionService.setInheritParentPermissions(recordCategory_1, false); + permissionService.setInheritParentPermissions(recordCategory_2, false); + permissionService.setPermission(recordCategory_1, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordCategory_2, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.UNFREEZE, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.UNFREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.UNFREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.UNFREEZE, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.UNFREEZE); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.UNFREEZE, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.UNFREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.UNFREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.UNFREEZE, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.UNFREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.UNFREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.UNFREEZE, AccessStatus.ALLOWED); + + permissionService.deletePermission(recordFolder_1, testers, RMPermissionModel.FILING); + permissionService.deletePermission(recordFolder_2, testers, RMPermissionModel.FILING); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(test_user, record_1, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, recordFolder_1, RMPermissionModel.UNFREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.UNFREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.UNFREEZE, AccessStatus.ALLOWED); + + // check frozen - can be in mutiple holds/freezes .. + + checkCapability(test_user, recordFolder_1, RMPermissionModel.UNFREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, record_1, RMPermissionModel.UNFREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, recordFolder_2, RMPermissionModel.UNFREEZE, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.UNFREEZE, AccessStatus.ALLOWED); + + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "unfreeze"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "unfreeze"); + + } + + public void testUpdateClassificationDatesCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.ALLOWED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.UPDATE_CLASSIFICATION_DATES, AccessStatus.DENIED); + } + + public void testUpdateExemptionCategoriesCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.ALLOWED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.UPDATE_EXEMPTION_CATEGORIES, AccessStatus.DENIED); + } + + public void testUpdateTriggerDatesCapability() + { + // TODO: waiting for action + } + + public void testUpdateVitalRecordCycleInformationCapability() + { + // TODO: ? + } + + public void testUpgradeDowngradeAndDeclassifyRecordsCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.UPGRADE_DOWNGRADE_AND_DECLASSIFY_RECORDS, AccessStatus.DENIED); + } + + public void testViewRecordsCapability() + { + // capability is checked above - just check permission assignments + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + checkPermission(rm_user, filePlan, RMPermissionModel.VIEW_RECORDS, AccessStatus.ALLOWED); + // already tested in many places above + } + + public void testViewUpdateReasonsForFreezeCapability() + { + // Folder + checkPermission(AuthenticationUtil.getSystemUserName(), filePlan, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + checkPermission(rm_administrator, filePlan, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + checkPermission(rm_records_manager, filePlan, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + checkPermission(rm_security_officer, filePlan, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkPermission(rm_power_user, filePlan, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkPermission(rm_user, filePlan, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + + // folder level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_1, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_1, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_1, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_1, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_1, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_1, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_1, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_administrator, record_1, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_1, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_1, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_power_user, record_1, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_user, record_1, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), recordFolder_2, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_administrator, recordFolder_2, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_records_manager, recordFolder_2, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_security_officer, recordFolder_2, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_power_user, recordFolder_2, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_user, recordFolder_2, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), record_2, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_administrator, record_2, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_records_manager, record_2, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_security_officer, record_2, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_power_user, record_2, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_user, record_2, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + Map params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "one"); + recordsManagementActionService.executeRecordsManagementAction(recordFolder_1, "freeze", params); + params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "Two"); + recordsManagementActionService.executeRecordsManagementAction(record_2, "freeze", params); + + // folder level + + checkCapability(AuthenticationUtil.getSystemUserName(), getHold(recordFolder_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + checkCapability(rm_administrator, getHold(recordFolder_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, getHold(recordFolder_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, getHold(recordFolder_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_power_user, getHold(recordFolder_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_user, getHold(recordFolder_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), getHold(record_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + checkCapability(rm_administrator, getHold(record_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, getHold(record_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, getHold(record_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_power_user, getHold(record_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_user, getHold(record_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + + // record level + + checkCapability(AuthenticationUtil.getSystemUserName(), getHold(recordFolder_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_administrator, getHold(recordFolder_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_records_manager, getHold(recordFolder_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_security_officer, getHold(recordFolder_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_power_user, getHold(recordFolder_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_user, getHold(recordFolder_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + + checkCapability(AuthenticationUtil.getSystemUserName(), getHold(record_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + checkCapability(rm_administrator, getHold(record_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + checkCapability(rm_records_manager, getHold(record_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + checkCapability(rm_security_officer, getHold(record_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_power_user, getHold(record_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(rm_user, getHold(record_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + + // check person with no access and add read and write + // Filing + + checkCapability(test_user, getHold(recordFolder_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, getHold(record_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, getHold(recordFolder_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, record_2, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + permissionService.setPermission(filePlan, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setInheritParentPermissions(recordCategory_1, false); + permissionService.setInheritParentPermissions(recordCategory_2, false); + permissionService.setPermission(recordCategory_1, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordCategory_2, testers, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, getHold(recordFolder_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, getHold(record_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, getHold(recordFolder_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, getHold(record_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, true); + + checkCapability(test_user, getHold(recordFolder_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, getHold(record_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, getHold(recordFolder_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, getHold(record_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE); + + checkCapability(test_user, getHold(recordFolder_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, getHold(record_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, getHold(recordFolder_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, getHold(record_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, true); + + checkCapability(test_user, getHold(recordFolder_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, getHold(record_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, getHold(recordFolder_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, getHold(record_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS); + + checkCapability(test_user, getHold(recordFolder_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, getHold(record_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, getHold(recordFolder_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, getHold(record_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_RECORDS, true); + + checkCapability(test_user, getHold(recordFolder_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, getHold(record_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, getHold(recordFolder_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, getHold(record_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + + permissionService.deletePermission(recordFolder_1, testers, RMPermissionModel.FILING); + permissionService.deletePermission(recordFolder_2, testers, RMPermissionModel.FILING); + + checkCapability(test_user, getHold(recordFolder_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, getHold(record_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, getHold(recordFolder_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, getHold(record_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + + permissionService.setPermission(recordFolder_1, testers, RMPermissionModel.FILING, true); + permissionService.setPermission(recordFolder_2, testers, RMPermissionModel.FILING, true); + + checkCapability(test_user, getHold(recordFolder_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, getHold(record_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, getHold(recordFolder_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, getHold(record_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + + // check frozen - can be in multiple holds/freezes .. + + checkCapability(test_user, getHold(recordFolder_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, getHold(record_1), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + checkCapability(test_user, getHold(recordFolder_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.DENIED); + checkCapability(test_user, getHold(record_2), RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, AccessStatus.ALLOWED); + + // TODO: property is not yet duplicated, waiting for action. + + // test filter - from the freeze object + + Map returned = publicNodeService.getProperties(getHold(recordFolder_1)); + assertTrue(returned.containsKey(RecordsManagementModel.PROP_HOLD_REASON)); + assertNotNull(publicNodeService.getProperty(getHold(recordFolder_1), RecordsManagementModel.PROP_HOLD_REASON)); + + permissionService.deletePermission(filePlan, testers, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE); + + returned = publicNodeService.getProperties(getHold(recordFolder_1)); + assertFalse(returned.containsKey(RecordsManagementModel.PROP_HOLD_REASON)); + try + { + publicNodeService.getProperty(getHold(recordFolder_1), RecordsManagementModel.PROP_HOLD_REASON); + fail(); + } + catch (AccessDeniedException ade) + { + + } + + // test query + + // update + + permissionService.setPermission(filePlan, testers, RMPermissionModel.FILING, true); + try + { + publicNodeService.setProperty(getHold(recordFolder_1), RecordsManagementModel.PROP_HOLD_REASON, "meep"); + fail(); + } + catch (AccessDeniedException ade) + { + + } + permissionService.setPermission(filePlan, testers, RMPermissionModel.VIEW_UPDATE_REASONS_FOR_FREEZE, true); + // TODO: fix reject by updateProperties - no capabilty lets it through even though not protected + // publicNodeService.setProperty(getHold(recordFolder_1), RecordsManagementModel.PROP_HOLD_REASON, "meep"); + + // update by action + + // + } + + private NodeRef getHold(NodeRef held) + { + List holdAssocs = nodeService.getChildAssocs(filePlan, RecordsManagementModel.ASSOC_HOLDS, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef holdAssoc : holdAssocs) + { + List freezeAssocs = nodeService.getChildAssocs(holdAssoc.getChildRef()); + for (ChildAssociationRef inHold : freezeAssocs) + { + if (inHold.getChildRef().equals(held)) + { + return holdAssoc.getChildRef(); + } + List heldFolderChildren = nodeService.getChildAssocs(inHold.getChildRef()); + for (ChildAssociationRef car : heldFolderChildren) + { + if (car.getChildRef().equals(held)) + { + return holdAssoc.getChildRef(); + } + } + } + } + return held; + } + + private void check(Map access, String name, AccessStatus accessStatus) + { + Capability capability = recordsManagementSecurityService.getCapability(name); + assertNotNull(capability); + assertEquals(accessStatus, access.get(capability)); + } + + private static ImporterBinding REPLACE_BINDING = new ImporterBinding() + { + + public UUID_BINDING getUUIDBinding() + { + return UUID_BINDING.UPDATE_EXISTING; + } + + public String getValue(String key) + { + return null; + } + + public boolean allowReferenceWithinTransaction() + { + return false; + } + + public QName[] getExcludedClasses() + { + return null; + } + + }; + +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/system/NotificationServiceHelperSystemTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/system/NotificationServiceHelperSystemTest.java new file mode 100644 index 0000000000..dbdcc71dc3 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/system/NotificationServiceHelperSystemTest.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2009-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.module.org_alfresco_module_rm.test.system; + +import java.util.ArrayList; +import java.util.List; + +import org.alfresco.module.org_alfresco_module_rm.notification.RecordsManagementNotificationHelper; +import org.alfresco.module.org_alfresco_module_rm.security.Role; +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.util.GUID; +import org.alfresco.util.PropertyMap; + + +/** + * Notification helper (system) test + * + * @author Roy Wetherall + */ +public class NotificationServiceHelperSystemTest extends BaseRMTestCase +{ + private static final String NOTIFICATION_ROLE = "RecordsManager"; + private static final String EMAIL_ADDRESS = "roy.wetherall@alfreso.com"; + + /** Services */ + private RecordsManagementNotificationHelper notificationHelper; + private MutableAuthenticationService authenticationService; + private PersonService personService; + private AuthorityService authorityService; + + /** Test data */ + private NodeRef record; + private List records; + private String userName; + private NodeRef person; + + @Override + protected void initServices() + { + super.initServices(); + + // Get the notification helper + notificationHelper = (RecordsManagementNotificationHelper)applicationContext.getBean("recordsManagementNotificationHelper"); + authenticationService = (MutableAuthenticationService)applicationContext.getBean("AuthenticationService"); + authorityService = (AuthorityService)applicationContext.getBean("AuthorityService"); + personService = (PersonService)applicationContext.getBean("PersonService"); + } + + @Override + protected void setupTestData() + { + super.setupTestData(); + + retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + + // Create a user + userName = GUID.generate(); + authenticationService.createAuthentication(userName, "".toCharArray()); + PropertyMap props = new PropertyMap(); + props.put(PROP_USERNAME, userName); + props.put(PROP_FIRSTNAME, "Test"); + props.put(PROP_LASTNAME, "User"); + props.put(PROP_EMAIL, EMAIL_ADDRESS); + person = personService.createPerson(props); + + // Find the authority for the given role + Role role = securityService.getRole(filePlan, NOTIFICATION_ROLE); + assertNotNull("Notification role could not be retrieved", role); + String roleGroup = role.getRoleGroupName(); + assertNotNull("Notification role group can not be null.", roleGroup); + + // Add user to notification role group + authorityService.addAuthority(roleGroup, userName); + + return null; + } + }); + } + + @Override + protected void setupTestDataImpl() + { + super.setupTestDataImpl(); + + // Create a few test records + record = utils.createRecord(rmFolder, "recordOne"); + NodeRef record2 = utils.createRecord(rmFolder, "recordTwo"); + NodeRef record3 = utils.createRecord(rmFolder, "recordThree"); + + records = new ArrayList(3); + records.add(record); + records.add(record2); + records.add(record3); + } + + @Override + protected void tearDownImpl() + { + super.tearDownImpl(); + + // Delete the person and user + personService.deletePerson(person); + } + + public void testSendDueForReviewNotification() + { + doTestInTransaction(new Test() + { + @Override + public Void run() + { + notificationHelper.recordsDueForReviewEmailNotification(records); + return null; + } + }); + } + + public void testSendSupersededNotification() + { + doTestInTransaction(new Test() + { + @Override + public Void run() + { + notificationHelper.recordSupersededEmailNotification(record); + return null; + } + }); + } +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/system/PerformanceDataLoadSystemTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/system/PerformanceDataLoadSystemTest.java new file mode 100644 index 0000000000..48de8c4a32 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/system/PerformanceDataLoadSystemTest.java @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.system; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.transaction.UserTransaction; + +import junit.framework.TestCase; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.module.org_alfresco_module_rm.dod5015.DOD5015Model; +import org.alfresco.module.org_alfresco_module_rm.identifier.IdentifierService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.Period; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.ApplicationContextHelper; +import org.springframework.context.ApplicationContext; + +/** + * @author Roy Wetherall + */ +public class PerformanceDataLoadSystemTest extends TestCase implements RecordsManagementModel, DOD5015Model +{ + private ApplicationContext appContext; + private AuthenticationComponent authenticationComponent; + private RecordsManagementService rmService; + private DispositionService dispositionService; + private TransactionService transactionService; + private NodeService nodeService; + private ContentService contentService; + private IdentifierService identifierService; + + UserTransaction userTransaction; + + private int SERIES_COUNT = 1; + private int CATEGORY_COUNT = 1; + private int RECORD_FOLDER_COUNT = 1; + private int RECORD_COUNT = 700; + + @Override + protected void setUp() throws Exception + { + appContext = ApplicationContextHelper.getApplicationContext(); + authenticationComponent = (AuthenticationComponent)appContext.getBean("authenticationComponent"); + transactionService = (TransactionService)appContext.getBean("transactionService"); + nodeService = (NodeService)appContext.getBean("nodeService"); + rmService = (RecordsManagementService)appContext.getBean("recordsManagementService"); + contentService = (ContentService)appContext.getBean("contentService"); + identifierService = (IdentifierService)appContext.getBean("identifierService"); + dispositionService = (DispositionService)appContext.getBean("dispositionService"); + + // Set authentication + authenticationComponent.setCurrentUser("admin"); + + // Start transaction + userTransaction = transactionService.getUserTransaction(); + userTransaction.begin(); + } + + @Override + protected void tearDown() throws Exception + { + userTransaction.commit(); + } + + public void testLoadTestData() throws Exception + { + // Get the file plan node + List roots = rmService.getFilePlans(); + if (roots.size() != 1) + { + fail("There is more than one root to load the test data into."); + } + NodeRef filePlan = roots.get(0); + + for (int i = 0; i < SERIES_COUNT; i++) + { + // Create the series + createSeries(filePlan, i); + } + } + + private void createSeries(NodeRef filePlan, int index) throws Exception + { + String name = genName("series-", index, "-" + System.currentTimeMillis()); + Map properties = new HashMap(2); + properties.put(ContentModel.PROP_NAME, name); + properties.put(PROP_IDENTIFIER, identifierService.generateIdentifier(TYPE_RECORD_CATEGORY, filePlan)); + NodeRef series = nodeService.createNode( + filePlan, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name), + TYPE_RECORD_CATEGORY, + properties).getChildRef(); + + System.out.println("Created series '" + name); + + // Create the categories + for (int i = 0; i < CATEGORY_COUNT; i++) + { + createCategory(series, i); + } + } + + private void createCategory(NodeRef series, int index) throws Exception + { + String name = genName("category-", index); + Map properties = new HashMap(7); + properties.put(ContentModel.PROP_NAME, name); + properties.put(ContentModel.PROP_DESCRIPTION, "Test category"); + NodeRef cat = nodeService.createNode( + series, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name), + TYPE_RECORD_CATEGORY, + properties).getChildRef(); + + // Need to close the transaction and reopen to kick off required initialisation behaviour + userTransaction.commit(); + userTransaction = transactionService.getUserTransaction(); + userTransaction.begin(); + + properties = nodeService.getProperties(cat); + //properties.put(PROP_IDENTIFIER, identifierService.generateIdentifier(series)); + properties.put(PROP_VITAL_RECORD_INDICATOR, true); + properties.put(PROP_REVIEW_PERIOD, new Period("week|1")); + nodeService.setProperties(cat, properties); + + // Get the disposition schedule + DispositionSchedule ds = dispositionService.getDispositionSchedule(cat); + properties = nodeService.getProperties(ds.getNodeRef()); + properties.put(PROP_DISPOSITION_AUTHORITY, "Disposition Authority"); + properties.put(PROP_DISPOSITION_INSTRUCTIONS, "Test disposition"); + nodeService.setProperties(ds.getNodeRef(), properties); + + // Add cutoff disposition action + Map actionParams = new HashMap(2); + actionParams.put(PROP_DISPOSITION_ACTION_NAME, "cutoff"); + actionParams.put(PROP_DISPOSITION_PERIOD, new Period("day|1")); + dispositionService.addDispositionActionDefinition(ds, actionParams); + + // Add delete disposition action + actionParams = new HashMap(3); + actionParams.put(PROP_DISPOSITION_ACTION_NAME, "destroy"); + actionParams.put(PROP_DISPOSITION_PERIOD, new Period("immediately|0")); + actionParams.put(PROP_DISPOSITION_PERIOD_PROPERTY, QName.createQName("{http://www.alfresco.org/model/recordsmanagement/1.0}cutOffDate")); + dispositionService.addDispositionActionDefinition(ds, actionParams); + + System.out.println("Created category '" + name); + + // Create the record folders + for (int i = 0; i < RECORD_FOLDER_COUNT; i++) + { + // Create the series + createRecordFolder(cat, i); + } + } + + private void createRecordFolder(NodeRef cat, int index) throws Exception + { + String name = genName("folder-", index); + Map properties = new HashMap(2); + properties.put(ContentModel.PROP_NAME, name); + properties.put(PROP_IDENTIFIER, identifierService.generateIdentifier(TYPE_RECORD_FOLDER, cat)); + NodeRef rf = nodeService.createNode( + cat, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name), + TYPE_RECORD_FOLDER, + properties).getChildRef(); + + // Need to close the transaction and reopen to kick off required initialisation behaviour + userTransaction.commit(); + userTransaction = transactionService.getUserTransaction(); + userTransaction.begin(); + + System.out.println("Created record folder '" + name); + + // Create the records + for (int i = 0; i < RECORD_COUNT; i++) + { + createRecord(rf, i); + } + + userTransaction.commit(); + userTransaction = transactionService.getUserTransaction(); + userTransaction.begin(); + } + + private void createRecord(NodeRef recordFolder, int index) throws Exception + { + String name = genName("record-", index, ".txt"); + Map properties = new HashMap(2); + properties.put(ContentModel.PROP_NAME, name); + NodeRef r = nodeService.createNode( + recordFolder, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name), + ContentModel.TYPE_CONTENT, + properties).getChildRef(); + ContentWriter cw = contentService.getWriter(r, ContentModel.PROP_CONTENT, true); + cw.setEncoding("UTF-8"); + cw.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + cw.putContent("This is my records content"); + + System.out.println("Created record '" + name); + } + + private String genName(String prefix, int index) + { + return genName(prefix, index, ""); + } + + private String genName(String prefix, int index, String postfix) + { + StringBuffer buff = new StringBuffer(120); + buff.append(prefix) + .append(index) + .append(postfix); + return buff.toString(); + } +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/system/RecordsManagementServiceImplSystemTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/system/RecordsManagementServiceImplSystemTest.java new file mode 100644 index 0000000000..7ca0a52d65 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/system/RecordsManagementServiceImplSystemTest.java @@ -0,0 +1,812 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.system; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.transaction.UserTransaction; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; +import org.alfresco.module.org_alfresco_module_rm.action.impl.BroadcastDispositionActionDefinitionUpdateAction; +import org.alfresco.module.org_alfresco_module_rm.action.impl.FileAction; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEvent; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.test.util.TestUtilities; +import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordDefinition; +import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordService; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.Period; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.BaseSpringTest; + +/** + * System test for records management service. + * + * Awaiting refactoring into records management test. + * + * @author Roy Wetherall + */ +public class RecordsManagementServiceImplSystemTest extends BaseSpringTest implements RecordsManagementModel +{ + protected static StoreRef SPACES_STORE = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore"); + + private NodeRef filePlan; + + private FileFolderService fileFolderService; + private NodeService nodeService; + private NodeService unprotectedNodeService; + private RecordsManagementActionService rmActionService; + private RecordsManagementService rmService; + private SearchService searchService; + private TransactionService transactionService; + private RetryingTransactionHelper transactionHelper; + private DispositionService dispositionService; + private VitalRecordService vitalRecordService; + + @Override + protected void onSetUpInTransaction() throws Exception + { + super.onSetUpInTransaction(); + + // Get the service required in the tests + this.fileFolderService = (FileFolderService)this.applicationContext.getBean("FileFolderService"); + this.nodeService = (NodeService)this.applicationContext.getBean("NodeService"); + this.unprotectedNodeService = (NodeService)this.applicationContext.getBean("nodeService"); + this.transactionService = (TransactionService)this.applicationContext.getBean("TransactionService"); + this.searchService = (SearchService)this.applicationContext.getBean("searchService"); + this.rmActionService = (RecordsManagementActionService)this.applicationContext.getBean("recordsManagementActionService"); + this.rmService = (RecordsManagementService)this.applicationContext.getBean("recordsManagementService"); + this.transactionHelper = (RetryingTransactionHelper)this.applicationContext.getBean("retryingTransactionHelper"); + this.dispositionService = (DispositionService)this.applicationContext.getBean("dispositionService"); + vitalRecordService = (VitalRecordService)applicationContext.getBean("VitalRecordService"); + + // Set the current security context as admin + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + + // Get the test data + setUpTestData(); + } + + private void setUpTestData() + { + filePlan = TestUtilities.loadFilePlanData(applicationContext); + } + + @Override + protected void onTearDownInTransaction() throws Exception + { + try + { + UserTransaction txn = transactionService.getUserTransaction(false); + txn.begin(); + this.nodeService.deleteNode(filePlan); + txn.commit(); + } + catch (Exception e) + { + // Nothing + //System.out.println("DID NOT DELETE FILE PLAN!"); + } + } + + public void testDispositionPresence() throws Exception + { + setComplete(); + endTransaction(); + + // create a record category node in + final NodeRef nodeRef = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + NodeRef rootNode = nodeService.getRootNode(SPACES_STORE); + Map props = new HashMap(1); + String recordCategoryName = "Test Record Category"; + props.put(ContentModel.PROP_NAME, recordCategoryName); + NodeRef result = nodeService.createNode(rootNode, ContentModel.ASSOC_CHILDREN, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, QName.createValidLocalName(recordCategoryName)), + TYPE_RECORD_CATEGORY, props).getChildRef(); + return result; + } + }); + + + // ensure the record category node has the scheduled aspect and the disposition schedule association + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + assertTrue(nodeService.hasAspect(nodeRef, RecordsManagementModel.ASPECT_SCHEDULED)); + List scheduleAssocs = nodeService.getChildAssocs(nodeRef, ASSOC_DISPOSITION_SCHEDULE, RegexQNamePattern.MATCH_ALL); + + + assertNotNull(scheduleAssocs); + assertEquals(1, scheduleAssocs.size()); + + // test retrieval of the disposition schedule via RM service + DispositionSchedule schedule = dispositionService.getDispositionSchedule(nodeRef); + assertNotNull(schedule); + return null; + } + }); + } + + /** + * This test method contains a subset of the tests in TC 7-2 of the DoD doc. + * @throws Exception + */ + public void testRescheduleRecord_IsNotCutOff() throws Exception + { + final NodeRef recCat = TestUtilities.getRecordCategory(rmService, nodeService, "Reports", "AIS Audit Records"); + // This RC has disposition instructions "Cut off monthly, hold 1 month, then destroy." + + setComplete(); + endTransaction(); + + // Create a suitable folder for this test. + final NodeRef testFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + Map folderProps = new HashMap(1); + String folderName = "testFolder" + System.currentTimeMillis(); + folderProps.put(ContentModel.PROP_NAME, folderName); + NodeRef recordFolder = nodeService.createNode(recCat, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, folderName), + TYPE_RECORD_FOLDER).getChildRef(); + return recordFolder; + } + }); + + // Create a record in the test folder. File it and declare it. + final NodeRef testRecord = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + final NodeRef result = nodeService.createNode(testFolder, ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, + "Record" + System.currentTimeMillis() + ".txt"), + ContentModel.TYPE_CONTENT).getChildRef(); + + rmActionService.executeRecordsManagementAction(result, "file"); + TestUtilities.declareRecord(result, unprotectedNodeService, rmActionService); + return result; + } + }); + + assertTrue("recCat missing scheduled aspect", nodeService.hasAspect(recCat, RecordsManagementModel.ASPECT_SCHEDULED)); + assertFalse("folder should not have scheduled aspect", nodeService.hasAspect(testFolder, RecordsManagementModel.ASPECT_SCHEDULED)); + assertFalse("record should not have scheduled aspect", nodeService.hasAspect(testRecord, RecordsManagementModel.ASPECT_SCHEDULED)); + + assertFalse("recCat should not have dispositionLifecycle aspect", nodeService.hasAspect(recCat, RecordsManagementModel.ASPECT_DISPOSITION_LIFECYCLE)); + assertTrue("testFolder missing dispositionLifecycle aspect", nodeService.hasAspect(testFolder, RecordsManagementModel.ASPECT_DISPOSITION_LIFECYCLE)); + assertFalse("testRecord should not have dispositionLifecycle aspect", nodeService.hasAspect(testRecord, RecordsManagementModel.ASPECT_DISPOSITION_LIFECYCLE)); + + // Change the cutoff conditions for the associated record category + final Date dateBeforeChange = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Date execute() throws Throwable + { + Date asOfDate = dispositionService.getNextDispositionAction(testFolder).getAsOfDate(); + System.out.println("Going to change the disposition asOf Date."); + System.out.println(" - Original value: " + asOfDate); + + // Now change "Cut off monthly, hold 1 month, then destroy." + // to "Cut off yearly, hold 1 month, then destroy." + List dads = dispositionService.getDispositionSchedule(testFolder).getDispositionActionDefinitions(); + DispositionActionDefinition firstDAD = dads.get(0); + assertEquals("cutoff", firstDAD.getName()); + NodeRef dadNode = firstDAD.getNodeRef(); + + nodeService.setProperty(dadNode, PROP_DISPOSITION_PERIOD, new Period("year|1")); + + List updatedProps = new ArrayList(1); + updatedProps.add(PROP_DISPOSITION_PERIOD); + refreshDispositionActionDefinition(dadNode, updatedProps); + + return asOfDate; + } + }); + + // view the record metadata to verify that the record has been rescheduled. + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + DispositionAction nextDispositionAction = dispositionService.getNextDispositionAction(testFolder); + + assertEquals("cutoff", nextDispositionAction.getName()); + Date asOfDateAfterChange = nextDispositionAction.getAsOfDate(); + System.out.println(" - Updated value: " + asOfDateAfterChange); + + assertFalse("Expected disposition asOf date to change.", asOfDateAfterChange.equals(dateBeforeChange)); + return null; + } + }); + + // Change the disposition type (e.g. time-based to event-based) + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + List rmes = dispositionService.getNextDispositionAction(testFolder).getDispositionActionDefinition().getEvents(); + System.out.println("Going to change the RMEs."); + System.out.println(" - Original value: " + rmes); + + List dads = dispositionService.getDispositionSchedule(testFolder).getDispositionActionDefinitions(); + DispositionActionDefinition firstDAD = dads.get(0); + assertEquals("cutoff", firstDAD.getName()); + NodeRef dadNode = firstDAD.getNodeRef(); + +// nodeService.setProperty(dadNode, PROP_DISPOSITION_PERIOD, null); + List eventNames= new ArrayList(); + eventNames.add("study_complete"); + nodeService.setProperty(dadNode, PROP_DISPOSITION_EVENT, (Serializable)eventNames); + + return null; + } + }); + // Now add a second event to the same + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + DispositionAction nextDispositionAction = dispositionService.getNextDispositionAction(testFolder); + StringBuilder buf = new StringBuilder(); + for (RecordsManagementEvent e : nextDispositionAction.getDispositionActionDefinition().getEvents()) { + buf.append(e.getName()).append(','); + } + + System.out.println("Going to change the RMEs again."); + System.out.println(" - Original value: " + buf.toString()); + + List dads = dispositionService.getDispositionSchedule(testFolder).getDispositionActionDefinitions(); + DispositionActionDefinition firstDAD = dads.get(0); + assertEquals("cutoff", firstDAD.getName()); + NodeRef dadNode = firstDAD.getNodeRef(); + + List eventNames= new ArrayList(); + eventNames.add("study_complete"); + eventNames.add("case_complete"); + nodeService.setProperty(dadNode, PROP_DISPOSITION_EVENT, (Serializable)eventNames); + + return null; + } + }); + + // View the record metadata to verify that the record has been rescheduled. + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + DispositionAction nextDispositionAction = dispositionService.getNextDispositionAction(testFolder); + + assertEquals("cutoff", nextDispositionAction.getName()); + StringBuilder buf = new StringBuilder(); + for (RecordsManagementEvent e : nextDispositionAction.getDispositionActionDefinition().getEvents()) { + buf.append(e.getName()).append(','); + } + System.out.println(" - Updated value: " + buf.toString()); + + assertFalse("Disposition should not be eligible.", nextDispositionAction.isEventsEligible()); + return null; + } + }); + + // Tidy up test nodes. + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + nodeService.deleteNode(testRecord); + + // Change the disposition Period back to what it was. + List dads = dispositionService.getDispositionSchedule(testFolder).getDispositionActionDefinitions(); + DispositionActionDefinition firstDAD = dads.get(0); + assertEquals("cutoff", firstDAD.getName()); + NodeRef dadNode = firstDAD.getNodeRef(); + nodeService.setProperty(dadNode, PROP_DISPOSITION_PERIOD, new Period("month|1")); + + nodeService.deleteNode(testFolder); + + return null; + } + }); + } + + private void refreshDispositionActionDefinition(NodeRef nodeRef, List updatedProps) + { + if (updatedProps != null) + { + Map params = new HashMap(); + params.put(BroadcastDispositionActionDefinitionUpdateAction.CHANGED_PROPERTIES, (Serializable)updatedProps); + rmActionService.executeRecordsManagementAction(nodeRef, BroadcastDispositionActionDefinitionUpdateAction.NAME, params); + } + + // Remove the unpublished update aspect + nodeService.removeAspect(nodeRef, ASPECT_UNPUBLISHED_UPDATE); + } + + public void testGetDispositionInstructions() throws Exception + { + setComplete(); + endTransaction(); + + // Get a record + // TODO + + // Get a record folder + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + NodeRef folderRecord = TestUtilities.getRecordFolder(rmService, nodeService, "Reports", "AIS Audit Records", "January AIS Audit Records"); + assertNotNull(folderRecord); + assertEquals("January AIS Audit Records", nodeService.getProperty(folderRecord, ContentModel.PROP_NAME)); + + assertFalse(rmService.isRecord(folderRecord)); + assertTrue(rmService.isRecordFolder(folderRecord)); + assertFalse(rmService.isRecordCategory(folderRecord)); + + DispositionSchedule di = dispositionService.getDispositionSchedule(folderRecord); + assertNotNull(di); + assertEquals("N1-218-00-4 item 023", di.getDispositionAuthority()); + assertEquals("Cut off monthly, hold 1 month, then destroy.", di.getDispositionInstructions()); + assertFalse(di.isRecordLevelDisposition()); + + // Get a record category + NodeRef recordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Reports", "AIS Audit Records"); + assertNotNull(recordCategory); + assertEquals("AIS Audit Records", nodeService.getProperty(recordCategory, ContentModel.PROP_NAME)); + + assertFalse(rmService.isRecord(recordCategory)); + assertFalse(rmService.isRecordFolder(recordCategory)); + assertTrue(rmService.isRecordCategory(recordCategory)); + + di = dispositionService.getDispositionSchedule(recordCategory); + assertNotNull(di); + assertEquals("N1-218-00-4 item 023", di.getDispositionAuthority()); + assertEquals("Cut off monthly, hold 1 month, then destroy.", di.getDispositionInstructions()); + assertFalse(di.isRecordLevelDisposition()); + + List das = di.getDispositionActionDefinitions(); + assertNotNull(das); + assertEquals(2, das.size()); + assertEquals("cutoff", das.get(0).getName()); + assertEquals("destroy", das.get(1).getName()); + return null; + } + }); + } + + public void testMoveRecordWithinFileplan() + { + setComplete(); + endTransaction(); + + // We need record folders for test-filing as follows: + // 1. A 'clean' record folder with no disposition schedult and no review period. + // 2. A 'vital' record folder which has a review period defined. + // 3. A 'dispositionable' record folder which has an applicable disposition schedule. + // + // The example fileplan includes a folder which covers [2] and [3] together. + + final NodeRef cleanRecordFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + NodeRef result = TestUtilities.getRecordFolder(rmService, nodeService, "Civilian Files", "Case Files and Papers", "Gilbert Competency Hearing"); + assertNotNull("cleanRecordFolder was null", result); + + final DispositionSchedule dispositionSchedule = dispositionService.getDispositionSchedule(result); + assertNull("cleanRecordFolder had non-null disposition instructions.", dispositionSchedule.getDispositionInstructions()); + assertTrue("cleanRecordFolder had non-empty disposition instruction definitions.", dispositionSchedule.getDispositionActionDefinitions().isEmpty()); + + final VitalRecordDefinition vitalRecordDefinition = vitalRecordService.getVitalRecordDefinition(result); + assertEquals("cleanRecordFolder had wrong review period.", "0", vitalRecordDefinition.getReviewPeriod().getExpression()); + assertNull("cleanRecordFolder had non-null review date.", vitalRecordDefinition.getNextReviewDate()); + return result; + } + }); + final NodeRef dispAndVitalRecordFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + NodeRef result = TestUtilities.getRecordFolder(rmService, nodeService, "Reports", "AIS Audit Records", "January AIS Audit Records"); + assertNotNull("dispositionAndVitalRecordFolder was null", result); + + final DispositionSchedule dispositionSchedule = dispositionService.getDispositionSchedule(result); + assertNotNull("dispositionAndVitalRecordFolder had null disposition instructions.", dispositionSchedule.getDispositionInstructions()); + assertFalse("dispositionAndVitalRecordFolder had empty disposition instruction definitions.", dispositionSchedule.getDispositionActionDefinitions().isEmpty()); + + final VitalRecordDefinition vitalRecordDefinition = vitalRecordService.getVitalRecordDefinition(result); + assertFalse("dispositionAndVitalRecordFolder had wrong review period.", "none|0".equals(vitalRecordDefinition.getReviewPeriod().getExpression())); + assertNotNull("dispositionAndVitalRecordFolder had null review date.", vitalRecordDefinition.getNextReviewDate()); + return result; + } + }); + + // Create a record in the 'clean' folder. + final NodeRef testRecord = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + final NodeRef result = nodeService.createNode(cleanRecordFolder, ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, + "Record" + System.currentTimeMillis() + ".txt"), + ContentModel.TYPE_CONTENT).getChildRef(); + + rmActionService.executeRecordsManagementAction(result, "file"); + TestUtilities.declareRecord(result, unprotectedNodeService, rmActionService); + return result; + } + }); + + // Ensure it's devoid of all disposition and review-related state. + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + final DispositionSchedule dispositionSchedule = dispositionService.getDispositionSchedule(testRecord); + assertNull("testRecord had non-null disposition instructions.", dispositionSchedule.getDispositionInstructions()); + assertTrue("testRecord had non-empty disposition instruction definitions.", dispositionSchedule.getDispositionActionDefinitions().isEmpty()); + + final VitalRecordDefinition vitalRecordDefinition = vitalRecordService.getVitalRecordDefinition(testRecord); + assertEquals("testRecord had wrong review period.", "0", vitalRecordDefinition.getReviewPeriod().getExpression()); + assertNull("testRecord had non-null review date.", vitalRecordDefinition.getNextReviewDate()); + return null; + } + }); + + // Move from non-vital to vital - also non-dispositionable to dispositionable at the same time. + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + nodeService.moveNode(testRecord, dispAndVitalRecordFolder, ContentModel.ASSOC_CONTAINS, ContentModel.ASSOC_CONTAINS); + return null; + } + }); + + // Assert that the disposition and review-related data are correct after the move. + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + final DispositionSchedule dispositionSchedule = dispositionService.getDispositionSchedule(testRecord); + assertNotNull("testRecord had null disposition instructions.", dispositionSchedule.getDispositionInstructions()); + assertFalse("testRecord had empty disposition instruction definitions.", dispositionSchedule.getDispositionActionDefinitions().isEmpty()); + + final VitalRecordDefinition vitalRecordDefinition = vitalRecordService.getVitalRecordDefinition(testRecord); + assertFalse("testRecord had wrong review period.", "0".equals(vitalRecordDefinition.getReviewPeriod().getExpression())); + assertNotNull("testRecord had null review date.", vitalRecordDefinition.getNextReviewDate()); + return null; + } + }); + + // Move the test record back from vital to non-vital - also dispositionable to non-dispositionable at the same time. + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + nodeService.moveNode(testRecord, cleanRecordFolder, ContentModel.ASSOC_CONTAINS, ContentModel.ASSOC_CONTAINS); + return null; + } + }); + + // Assert that the disposition and review-related data are correct after the move. + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + final DispositionSchedule dispositionSchedule = dispositionService.getDispositionSchedule(testRecord); + assertNull("testRecord had non-null disposition instructions.", dispositionSchedule.getDispositionInstructions()); + assertTrue("testRecord had non-empty disposition instruction definitions.", dispositionSchedule.getDispositionActionDefinitions().isEmpty()); + + final VitalRecordDefinition vitalRecordDefinition = vitalRecordService.getVitalRecordDefinition(testRecord); + assertEquals("testRecord had wrong review period.", "0", vitalRecordDefinition.getReviewPeriod().getExpression()); + assertNull("testRecord had non-null review date.", vitalRecordDefinition.getNextReviewDate()); + return null; + } + }); + + //TODO check the search aspect + + // Tidy up. + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + nodeService.deleteNode(testRecord); + + return null; + } + }); + } + + public void testCopyRecordWithinFileplan() + { + setComplete(); + endTransaction(); + + // We need record folders for test-filing as follows: + // 1. A 'clean' record folder with no disposition schedule and no review period. + // 2. A 'vital' record folder which has a review period defined. + // 3. A 'dispositionable' record folder which has an applicable disposition schedule. + // + // The example fileplan includes a folder which covers [2] and [3] together. + + final NodeRef cleanRecordFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + NodeRef result = TestUtilities.getRecordFolder(rmService, nodeService, "Civilian Files", "Case Files and Papers", "Gilbert Competency Hearing"); + assertNotNull("cleanRecordFolder was null", result); + + final DispositionSchedule dispositionSchedule = dispositionService.getDispositionSchedule(result); + assertNull("cleanRecordFolder had non-null disposition instructions.", dispositionSchedule.getDispositionInstructions()); + assertTrue("cleanRecordFolder had non-empty disposition instruction definitions.", dispositionSchedule.getDispositionActionDefinitions().isEmpty()); + + final VitalRecordDefinition vitalRecordDefinition = vitalRecordService.getVitalRecordDefinition(result); + assertEquals("cleanRecordFolder had wrong review period.", "0", vitalRecordDefinition.getReviewPeriod().getExpression()); + assertNull("cleanRecordFolder had non-null review date.", vitalRecordDefinition.getNextReviewDate()); + return result; + } + }); + final NodeRef dispAndVitalRecordFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + NodeRef result = TestUtilities.getRecordFolder(rmService, nodeService, "Reports", "AIS Audit Records", "January AIS Audit Records"); + assertNotNull("dispositionAndVitalRecordFolder was null", result); + + final DispositionSchedule dispositionSchedule = dispositionService.getDispositionSchedule(result); + assertNotNull("dispositionAndVitalRecordFolder had null disposition instructions.", dispositionSchedule.getDispositionInstructions()); + assertFalse("dispositionAndVitalRecordFolder had empty disposition instruction definitions.", dispositionSchedule.getDispositionActionDefinitions().isEmpty()); + + final VitalRecordDefinition vitalRecordDefinition = vitalRecordService.getVitalRecordDefinition(result); + assertFalse("dispositionAndVitalRecordFolder had wrong review period.", "none|0".equals(vitalRecordDefinition.getReviewPeriod().getExpression())); + assertNotNull("dispositionAndVitalRecordFolder had null review date.", vitalRecordDefinition.getNextReviewDate()); + return result; + } + }); + + // Create a record in the 'clean' folder. + final NodeRef testRecord = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + final NodeRef result = nodeService.createNode(cleanRecordFolder, ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, + "Record" + System.currentTimeMillis() + ".txt"), + ContentModel.TYPE_CONTENT).getChildRef(); + + rmActionService.executeRecordsManagementAction(result, "file"); + TestUtilities.declareRecord(result, unprotectedNodeService, rmActionService); + return result; + } + }); + + // Ensure it's devoid of all disposition and review-related state. + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + final DispositionSchedule dispositionSchedule = dispositionService.getDispositionSchedule(testRecord); + assertNull("testRecord had non-null disposition instructions.", dispositionSchedule.getDispositionInstructions()); + assertTrue("testRecord had non-empty disposition instruction definitions.", dispositionSchedule.getDispositionActionDefinitions().isEmpty()); + + final VitalRecordDefinition vitalRecordDefinition = vitalRecordService.getVitalRecordDefinition(testRecord); + assertEquals("testRecord had wrong review period.", "0", vitalRecordDefinition.getReviewPeriod().getExpression()); + assertNull("testRecord had non-null review date.", vitalRecordDefinition.getNextReviewDate()); + return null; + } + }); + + // Copy from non-vital to vital - also non-dispositionable to dispositionable at the same time. + final NodeRef copiedNode = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + FileInfo fileInfo = fileFolderService.copy(testRecord, dispAndVitalRecordFolder, null); + NodeRef n = fileInfo.getNodeRef(); + return n; + } + }); + + // Assert that the disposition and review-related data are correct after the copy. + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + final DispositionSchedule dispositionSchedule = dispositionService.getDispositionSchedule(copiedNode); + assertNotNull("copiedNode had null disposition instructions.", dispositionSchedule.getDispositionInstructions()); + assertFalse("copiedNode had empty disposition instruction definitions.", dispositionSchedule.getDispositionActionDefinitions().isEmpty()); + + final VitalRecordDefinition vitalRecordDefinition = vitalRecordService.getVitalRecordDefinition(copiedNode); + assertFalse("copiedNode had wrong review period.", "0".equals(vitalRecordDefinition.getReviewPeriod().getExpression())); + assertNotNull("copiedNode had null review date.", vitalRecordDefinition.getNextReviewDate()); + return null; + } + }); + + // Create a record in the 'vital and disposition' folder. + final NodeRef testRecord2 = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + final NodeRef result = nodeService.createNode(dispAndVitalRecordFolder, ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, + "Record2" + System.currentTimeMillis() + ".txt"), + ContentModel.TYPE_CONTENT).getChildRef(); + + rmActionService.executeRecordsManagementAction(result, "file"); + TestUtilities.declareRecord(result, unprotectedNodeService, rmActionService); + return result; + } + }); + + // Check the vital and disposition status. + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + final DispositionSchedule dispositionSchedule = dispositionService.getDispositionSchedule(testRecord2); + assertNotNull("testRecord2 had null disposition instructions.", dispositionSchedule.getDispositionInstructions()); + assertFalse("testRecord2 had empty disposition instruction definitions.", dispositionSchedule.getDispositionActionDefinitions().isEmpty()); + + final VitalRecordDefinition vitalRecordDefinition = vitalRecordService.getVitalRecordDefinition(testRecord2); + assertFalse("testRecord2 had wrong review period.", "0".equals(vitalRecordDefinition.getReviewPeriod().getExpression())); + assertNotNull("testRecord2 had null review date.", vitalRecordDefinition.getNextReviewDate()); + return null; + } + }); + + // copy the record back from vital to non-vital - also dispositionable to non-dispositionable at the same time. + final NodeRef copiedBackNode = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + FileInfo fileInfo = fileFolderService.copy(testRecord2, cleanRecordFolder, null); // TODO Something wrong here. + NodeRef n = fileInfo.getNodeRef(); + return n; + } + }); + + // Assert that the disposition and review-related data are correct after the copy-back. + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + final DispositionSchedule dispositionSchedule = dispositionService.getDispositionSchedule(copiedBackNode); + assertNull("copiedBackNode had non-null disposition instructions.", dispositionSchedule.getDispositionInstructions()); + assertTrue("copiedBackNode had non-empty disposition instruction definitions.", dispositionSchedule.getDispositionActionDefinitions().isEmpty()); + + final VitalRecordDefinition vitalRecordDefinition = vitalRecordService.getVitalRecordDefinition(copiedBackNode); + assertEquals("copiedBackNode had wrong review period.", "0", vitalRecordDefinition.getReviewPeriod().getExpression()); + assertNull("copiedBackNode had non-null review date.", vitalRecordDefinition.getNextReviewDate()); + return null; + } + }); + + //TODO check the search aspect + + // Tidy up. + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + nodeService.deleteNode(copiedBackNode); + nodeService.deleteNode(testRecord2); + nodeService.deleteNode(copiedNode); + nodeService.deleteNode(testRecord); + + return null; + } + }); + } + + public void xxxtestUpdateNextDispositionAction() + { + setComplete(); + endTransaction(); + + final FileAction fileAction = (FileAction)applicationContext.getBean("file"); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + // Get a record folder + NodeRef folderRecord = TestUtilities.getRecordFolder(rmService, nodeService, "Reports", "AIS Audit Records", "January AIS Audit Records"); + assertNotNull(folderRecord); + assertEquals("January AIS Audit Records", nodeService.getProperty(folderRecord, ContentModel.PROP_NAME)); + + DispositionSchedule di = dispositionService.getDispositionSchedule(folderRecord); + assertNotNull(di); + assertEquals("N1-218-00-4 item 023", di.getDispositionAuthority()); + assertEquals("Cut off monthly, hold 1 month, then destroy.", di.getDispositionInstructions()); + assertFalse(di.isRecordLevelDisposition()); + + assertFalse(nodeService.hasAspect(folderRecord, ASPECT_DISPOSITION_LIFECYCLE)); + + fileAction.updateNextDispositionAction(folderRecord); + + + // Check the next disposition action + assertTrue(nodeService.hasAspect(folderRecord, ASPECT_DISPOSITION_LIFECYCLE)); + NodeRef ndNodeRef = nodeService.getChildAssocs(folderRecord, ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + assertNotNull(ndNodeRef); + assertEquals("cutoff", nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION)); + assertEquals(di.getDispositionActionDefinitions().get(0).getId(), nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION_ID)); + assertNotNull(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF)); + + // Check the history is empty + // TODO + + Map props = new HashMap(1); + props.put(PROP_CUT_OFF_DATE, new Date()); + unprotectedNodeService.addAspect(folderRecord, ASPECT_CUT_OFF, props); + fileAction.updateNextDispositionAction(folderRecord); + + assertTrue(nodeService.hasAspect(folderRecord, ASPECT_DISPOSITION_LIFECYCLE)); + ndNodeRef = nodeService.getChildAssocs(folderRecord, ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); + assertNotNull(ndNodeRef); + assertEquals("destroy", nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION)); + assertEquals(di.getDispositionActionDefinitions().get(1).getId(), nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION_ID)); + assertNotNull(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF)); + + // Check the history has an action + // TODO + + fileAction.updateNextDispositionAction(folderRecord); + + assertTrue(nodeService.hasAspect(folderRecord, ASPECT_DISPOSITION_LIFECYCLE)); + assertTrue(nodeService.getChildAssocs(folderRecord, ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).isEmpty()); + + // Check the history has both actions + // TODO + return null; + } + }); + } +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java new file mode 100644 index 0000000000..28a10feff4 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java @@ -0,0 +1,491 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.util; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; +import org.alfresco.module.org_alfresco_module_rm.action.impl.FreezeAction; +import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.model.RmSiteType; +import org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchService; +import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService; +import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordService; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.cmr.site.SiteVisibility; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ApplicationContextHelper; +import org.alfresco.util.GUID; +import org.alfresco.util.RetryingTransactionHelperTestCase; +import org.springframework.context.ApplicationContext; + +/** + * Base test case class to use for RM unit tests. + * + * @author Roy Wetherall + */ +public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase + implements RecordsManagementModel, ContentModel +{ + /** Application context */ + protected static final String[] CONFIG_LOCATIONS = new String[] + { + "classpath:alfresco/application-context.xml", + "classpath:test-context.xml" + }; + protected ApplicationContext applicationContext; + + /** Test model contants */ + protected String URI = "http://www.alfresco.org/model/rmtest/1.0"; + protected String PREFIX = "rmt"; + protected QName TYPE_CUSTOM_TYPE = QName.createQName(URI, "customType"); + protected QName ASPECT_CUSTOM_ASPECT = QName.createQName(URI, "customAspect"); + protected QName ASPECT_RECORD_META_DATA = QName.createQName(URI, "recordMetaData"); + + /** Site id */ + protected static final String SITE_ID = "mySite"; + + /** Common test utils */ + protected CommonRMTestUtils utils; + + /** Services */ + protected NodeService nodeService; + protected ContentService contentService; + protected DictionaryService dictionaryService; + protected RetryingTransactionHelper retryingTransactionHelper; + protected PolicyComponent policyComponent; + protected NamespaceService namespaceService; + protected SearchService searchService; + protected SiteService siteService; + protected MutableAuthenticationService authenticationService; + protected AuthorityService authorityService; + protected PersonService personService; + + /** RM Services */ + protected RecordsManagementService rmService; + protected DispositionService dispositionService; + protected RecordsManagementEventService eventService; + protected RecordsManagementAdminService adminService; + protected RecordsManagementActionService actionService; + protected RecordsManagementSearchService rmSearchService; + protected RecordsManagementSecurityService securityService; + protected CapabilityService capabilityService; + protected VitalRecordService vitalRecordService; + + /** test data */ + protected StoreRef storeRef; + protected NodeRef rootNodeRef; + protected SiteInfo siteInfo; + protected NodeRef folder; + protected NodeRef filePlan; + protected NodeRef rmContainer; + protected DispositionSchedule dispositionSchedule; + protected NodeRef rmFolder; + + /** multi-hierarchy test data + * + * |--rmRootContainer + * | + * |--mhContainer + * | + * |--mhContainer-1-1 (has schedule - folder level) + * | | + * | |--mhContainer-2-1 + * | | + * | |--mhContainer-3-1 + * | + * |--mhContainer-1-2 (has schedule - folder level) + * | + * |--mhContainer-2-2 + * | | + * | |--mhContainer-3-2 + * | | + * | |--mhContainer-3-3 (has schedule - record level) + * | + * |--mhContainer-2-3 (has schedule - folder level) + * | + * |--mhContainer-3-4 + * | + * |--mhContainer-3-5 (has schedule- record level) + */ + + protected NodeRef mhContainer; + + protected NodeRef mhContainer11; + protected DispositionSchedule mhDispositionSchedule11; + protected NodeRef mhContainer12; + protected DispositionSchedule mhDispositionSchedule12; + + protected NodeRef mhContainer21; + protected NodeRef mhContainer22; + protected NodeRef mhContainer23; + protected DispositionSchedule mhDispositionSchedule23; + + protected NodeRef mhContainer31; + protected NodeRef mhContainer32; + protected NodeRef mhContainer33; + protected DispositionSchedule mhDispositionSchedule33; + protected NodeRef mhContainer34; + protected NodeRef mhContainer35; + protected DispositionSchedule mhDispositionSchedule35; + + protected NodeRef mhRecordFolder41; + protected NodeRef mhRecordFolder42; + protected NodeRef mhRecordFolder43; + protected NodeRef mhRecordFolder44; + protected NodeRef mhRecordFolder45; + + /** test user names */ + protected String[] testUsers; + protected String userName; + protected String rmUserName; + protected String powerUserName; + protected String securityOfficerName; + protected String recordsManagerName; + protected String rmAdminName; + + /** test people */ + protected NodeRef userPerson; + protected NodeRef rmUserPerson; + protected NodeRef powerUserPerson; + protected NodeRef securityOfficerPerson; + protected NodeRef recordsManagerPerson; + protected NodeRef rmAdminPerson; + + /** + * Indicates whether this is a multi-hierarchy test or not. If it is then the multi-hierarchy record + * taxonomy test data is loaded. + */ + protected boolean isMultiHierarchyTest() + { + return false; + } + + /** + * Indicates whether the test users should be created or not. + * @return + */ + protected boolean isUserTest() + { + return false; + } + + /** + * @see junit.framework.TestCase#setUp() + */ + @Override + protected void setUp() throws Exception + { + // Get the application context + applicationContext = ApplicationContextHelper.getApplicationContext(CONFIG_LOCATIONS); + utils = new CommonRMTestUtils(applicationContext); + + // Initialise the service beans + initServices(); + + // Setup test data + setupTestData(); + if (isMultiHierarchyTest() == true) + { + setupMultiHierarchyTestData(); + } + // Create the users here + if (isUserTest() == true) + { + setupTestUsers(filePlan); + } + } + + /** + * Initialise the service beans. + */ + protected void initServices() + { + // Get services + nodeService = (NodeService)applicationContext.getBean("NodeService"); + contentService = (ContentService)applicationContext.getBean("ContentService"); + retryingTransactionHelper = (RetryingTransactionHelper)applicationContext.getBean("retryingTransactionHelper"); + namespaceService = (NamespaceService)this.applicationContext.getBean("NamespaceService"); + searchService = (SearchService)this.applicationContext.getBean("SearchService"); + policyComponent = (PolicyComponent)this.applicationContext.getBean("policyComponent"); + dictionaryService = (DictionaryService)this.applicationContext.getBean("DictionaryService"); + siteService = (SiteService)this.applicationContext.getBean("SiteService"); + authorityService = (AuthorityService)this.applicationContext.getBean("AuthorityService"); + authenticationService = (MutableAuthenticationService)this.applicationContext.getBean("AuthenticationService"); + personService = (PersonService)this.applicationContext.getBean("PersonService"); + + // Get RM services + rmService = (RecordsManagementService)applicationContext.getBean("RecordsManagementService"); + dispositionService = (DispositionService)applicationContext.getBean("DispositionService"); + eventService = (RecordsManagementEventService)applicationContext.getBean("RecordsManagementEventService"); + adminService = (RecordsManagementAdminService)applicationContext.getBean("RecordsManagementAdminService"); + actionService = (RecordsManagementActionService)this.applicationContext.getBean("RecordsManagementActionService"); + rmSearchService = (RecordsManagementSearchService)this.applicationContext.getBean("RecordsManagementSearchService"); + securityService = (RecordsManagementSecurityService)this.applicationContext.getBean("RecordsManagementSecurityService"); + capabilityService = (CapabilityService)this.applicationContext.getBean("CapabilityService"); + vitalRecordService = (VitalRecordService)this.applicationContext.getBean("VitalRecordService"); + } + + /** + * @see junit.framework.TestCase#tearDown() + */ + @Override + protected void tearDown() throws Exception + { + retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + // As system user + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + + // Do the tear down + tearDownImpl(); + + return null; + } + }); + } + + /** + * Tear down implementation + */ + protected void tearDownImpl() + { + // Delete the folder + nodeService.deleteNode(folder); + + // Delete the site + siteService.deleteSite(SITE_ID); + } + + /** + * @see org.alfresco.util.RetryingTransactionHelperTestCase#getRetryingTransactionHelper() + */ + @Override + public RetryingTransactionHelper getRetryingTransactionHelper() + { + return retryingTransactionHelper; + } + + /** + * Setup test data for tests + */ + protected void setupTestData() + { + retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + setupTestDataImpl(); + return null; + } + }); + } + + /** + * Impl of test data setup + */ + protected void setupTestDataImpl() + { + storeRef = StoreRef.STORE_REF_WORKSPACE_SPACESSTORE; + rootNodeRef = nodeService.getRootNode(storeRef); + + // Create folder + String containerName = "RM2_" + System.currentTimeMillis(); + Map containerProps = new HashMap(1); + containerProps.put(ContentModel.PROP_NAME, containerName); + folder = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, containerName), + ContentModel.TYPE_FOLDER, + containerProps).getChildRef(); + assertNotNull("Could not create base folder", folder); + + // Create the site + siteInfo = siteService.createSite("preset", SITE_ID, "title", "descrition", SiteVisibility.PUBLIC, RecordsManagementModel.TYPE_RM_SITE); + filePlan = siteService.getContainer(SITE_ID, RmSiteType.COMPONENT_DOCUMENT_LIBRARY); + assertNotNull("Site document library container was not created successfully.", filePlan); + + // Create RM container + rmContainer = rmService.createRecordCategory(filePlan, "rmContainer"); + assertNotNull("Could not create rm container", rmContainer); + + // Create disposition schedule + dispositionSchedule = utils.createBasicDispositionSchedule(rmContainer); + + // Create RM folder + rmFolder = rmService.createRecordFolder(rmContainer, "rmFolder"); + assertNotNull("Could not create rm folder", rmFolder); + } + + protected void setupTestUsers(final NodeRef filePlan) + { + retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + setupTestUsersImpl(filePlan); + return null; + } + }); + } + + /** + * + * @param filePlan + */ + protected void setupTestUsersImpl(NodeRef filePlan) + { + userName = GUID.generate(); + userPerson = createPerson(userName); + + rmUserName = GUID.generate(); + rmUserPerson = createPerson(rmUserName); + securityService.assignRoleToAuthority(filePlan, "User", rmUserName); + + powerUserName = GUID.generate(); + powerUserPerson = createPerson(powerUserName); + securityService.assignRoleToAuthority(filePlan, "PowerUser", powerUserName); + + securityOfficerName = GUID.generate(); + securityOfficerPerson = createPerson(securityOfficerName); + securityService.assignRoleToAuthority(filePlan, "SecurityOfficer", securityOfficerName); + + recordsManagerName = GUID.generate(); + recordsManagerPerson = createPerson(recordsManagerName); + securityService.assignRoleToAuthority(filePlan, "RecordsManager", recordsManagerName); + + rmAdminName = GUID.generate(); + rmAdminPerson = createPerson(rmAdminName); + securityService.assignRoleToAuthority(filePlan, "Administrator", rmAdminName); + + testUsers = new String[] + { + userName, + rmUserName, + powerUserName, + securityOfficerName, + recordsManagerName, + rmAdminName + }; + } + + protected NodeRef createPerson(String userName) + { + authenticationService.createAuthentication(userName, "password".toCharArray()); + Map properties = new HashMap(); + properties.put(ContentModel.PROP_USERNAME, userName); + return personService.createPerson(properties); + } + + /** + * Setup multi hierarchy test data + */ + protected void setupMultiHierarchyTestData() + { + retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + // As system user + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + + // Do setup + setupMultiHierarchyTestDataImpl(); + + return null; + } + }); + } + + /** + * Impl of multi hierarchy test data + */ + protected void setupMultiHierarchyTestDataImpl() + { + // Create root mh container + mhContainer = rmService.createRecordCategory(filePlan, "mhContainer"); + + // Level 1 + mhContainer11 = rmService.createRecordCategory(mhContainer, "mhContainer11"); + mhDispositionSchedule11 = utils.createBasicDispositionSchedule(mhContainer11, "ds11", utils.DEFAULT_DISPOSITION_AUTHORITY, false, true); + mhContainer12 = rmService.createRecordCategory(mhContainer, "mhContainer12"); + mhDispositionSchedule12 = utils.createBasicDispositionSchedule(mhContainer12, "ds12", utils.DEFAULT_DISPOSITION_AUTHORITY, false, true); + + // Level 2 + mhContainer21 = rmService.createRecordCategory(mhContainer11, "mhContainer21"); + mhContainer22 = rmService.createRecordCategory(mhContainer12, "mhContainer22"); + mhContainer23 = rmService.createRecordCategory(mhContainer12, "mhContainer23"); + mhDispositionSchedule23 = utils.createBasicDispositionSchedule(mhContainer23, "ds23", utils.DEFAULT_DISPOSITION_AUTHORITY, false, true); + + // Level 3 + mhContainer31 = rmService.createRecordCategory(mhContainer21, "mhContainer31"); + mhContainer32 = rmService.createRecordCategory(mhContainer22, "mhContainer32"); + mhContainer33 = rmService.createRecordCategory(mhContainer22, "mhContainer33"); + mhDispositionSchedule33 = utils.createBasicDispositionSchedule(mhContainer33, "ds33", utils.DEFAULT_DISPOSITION_AUTHORITY, true, true); + mhContainer34 = rmService.createRecordCategory(mhContainer23, "mhContainer34"); + mhContainer35 = rmService.createRecordCategory(mhContainer23, "mhContainer35"); + mhDispositionSchedule35 = utils.createBasicDispositionSchedule(mhContainer35, "ds35", utils.DEFAULT_DISPOSITION_AUTHORITY, true, true); + + // Record folders + mhRecordFolder41 = rmService.createRecordFolder(mhContainer31, "mhFolder41"); + mhRecordFolder42 = rmService.createRecordFolder(mhContainer32, "mhFolder42"); + mhRecordFolder43 = rmService.createRecordFolder(mhContainer33, "mhFolder43"); + mhRecordFolder44 = rmService.createRecordFolder(mhContainer34, "mhFolder44"); + mhRecordFolder45 = rmService.createRecordFolder(mhContainer35, "mhFolder45"); + } +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMWebScriptTestCase.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMWebScriptTestCase.java new file mode 100644 index 0000000000..f45947b6f1 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMWebScriptTestCase.java @@ -0,0 +1,236 @@ +/** + * + */ +package org.alfresco.module.org_alfresco_module_rm.test.util; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; +import org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditService; +import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.model.RmSiteType; +import org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchService; +import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService; +import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordService; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.repo.web.scripts.BaseWebScriptTest; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.Period; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.cmr.site.SiteVisibility; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.springframework.context.ApplicationContext; + +/** + * @author Roy Wetherall + */ +public class BaseRMWebScriptTestCase extends BaseWebScriptTest +{ + /** Site id */ + protected static final String SITE_ID = "mySite"; + + /** Common test utils */ + protected CommonRMTestUtils utils; + + /** Services */ + protected NodeService nodeService; + protected ContentService contentService; + protected DictionaryService dictionaryService; + protected RetryingTransactionHelper retryingTransactionHelper; + protected PolicyComponent policyComponent; + protected NamespaceService namespaceService; + protected SearchService searchService; + protected SiteService siteService; + protected MutableAuthenticationService authenticationService; + protected AuthorityService authorityService; + protected PersonService personService; + + /** RM Services */ + protected RecordsManagementService rmService; + protected DispositionService dispositionService; + protected RecordsManagementEventService eventService; + protected RecordsManagementAdminService adminService; + protected RecordsManagementActionService actionService; + protected RecordsManagementSearchService rmSearchService; + protected RecordsManagementSecurityService securityService; + protected RecordsManagementAuditService auditService; + protected CapabilityService capabilityService; + protected VitalRecordService vitalRecordService; + + /** test data */ + protected StoreRef storeRef; + protected NodeRef rootNodeRef; + protected SiteInfo siteInfo; + protected NodeRef folder; + protected NodeRef filePlan; + protected NodeRef recordSeries; // A category with no disposition schedule + protected NodeRef recordCategory; + protected DispositionSchedule dispositionSchedule; + protected NodeRef recordFolder; + protected NodeRef recordFolder2; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + // Initialise the service beans + initServices(); + + // Setup test data + setupTestData(); + } + + /** + * Initialise the service beans. + */ + protected void initServices() + { + ApplicationContext applicationContext = getServer().getApplicationContext(); + + // Common test utils + utils = new CommonRMTestUtils(applicationContext); + + // Get services + nodeService = (NodeService)applicationContext.getBean("NodeService"); + contentService = (ContentService)applicationContext.getBean("ContentService"); + retryingTransactionHelper = (RetryingTransactionHelper)applicationContext.getBean("retryingTransactionHelper"); + namespaceService = (NamespaceService)applicationContext.getBean("NamespaceService"); + searchService = (SearchService)applicationContext.getBean("SearchService"); + policyComponent = (PolicyComponent)applicationContext.getBean("policyComponent"); + dictionaryService = (DictionaryService)applicationContext.getBean("DictionaryService"); + siteService = (SiteService)applicationContext.getBean("SiteService"); + authorityService = (AuthorityService)applicationContext.getBean("AuthorityService"); + authenticationService = (MutableAuthenticationService)applicationContext.getBean("AuthenticationService"); + personService = (PersonService)applicationContext.getBean("PersonService"); + + // Get RM services + rmService = (RecordsManagementService)applicationContext.getBean("RecordsManagementService"); + dispositionService = (DispositionService)applicationContext.getBean("DispositionService"); + eventService = (RecordsManagementEventService)applicationContext.getBean("RecordsManagementEventService"); + adminService = (RecordsManagementAdminService)applicationContext.getBean("RecordsManagementAdminService"); + actionService = (RecordsManagementActionService)applicationContext.getBean("RecordsManagementActionService"); + rmSearchService = (RecordsManagementSearchService)applicationContext.getBean("RecordsManagementSearchService"); + securityService = (RecordsManagementSecurityService)applicationContext.getBean("RecordsManagementSecurityService"); + auditService = (RecordsManagementAuditService)applicationContext.getBean("RecordsManagementAuditService"); + capabilityService = (CapabilityService)applicationContext.getBean("CapabilityService"); + vitalRecordService = (VitalRecordService)applicationContext.getBean("VitalRecordService"); + } + + /** + * @see junit.framework.TestCase#tearDown() + */ + @Override + protected void tearDown() throws Exception + { + retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + // As system user + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + + // Do the tear down + tearDownImpl(); + + return null; + } + }); + } + + /** + * Tear down implementation + */ + protected void tearDownImpl() + { + // Delete the folder + nodeService.deleteNode(folder); + + // Delete the site + siteService.deleteSite(SITE_ID); + } + + /** + * Setup test data for tests + */ + protected void setupTestData() + { + retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + setupTestDataImpl(); + return null; + } + }); + } + + /** + * Impl of test data setup + */ + protected void setupTestDataImpl() + { + storeRef = StoreRef.STORE_REF_WORKSPACE_SPACESSTORE; + rootNodeRef = nodeService.getRootNode(storeRef); + + // Create folder + String containerName = "RM2_" + System.currentTimeMillis(); + Map containerProps = new HashMap(1); + containerProps.put(ContentModel.PROP_NAME, containerName); + folder = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, containerName), + ContentModel.TYPE_FOLDER, + containerProps).getChildRef(); + assertNotNull("Could not create base folder", folder); + + // Create the site + siteInfo = siteService.createSite("preset", SITE_ID, "title", "descrition", SiteVisibility.PUBLIC, RecordsManagementModel.TYPE_RM_SITE); + filePlan = siteService.getContainer(SITE_ID, RmSiteType.COMPONENT_DOCUMENT_LIBRARY); + assertNotNull("Site document library container was not created successfully.", filePlan); + + recordSeries = rmService.createRecordCategory(filePlan, "recordSeries"); + assertNotNull("Could not create record category with no disposition schedule", recordSeries); + + recordCategory = rmService.createRecordCategory(recordSeries, "rmContainer"); + assertNotNull("Could not create record category", recordCategory); + + // Make vital record + vitalRecordService.setVitalRecordDefintion(recordCategory, true, new Period("week|1")); + + // Create disposition schedule + dispositionSchedule = utils.createBasicDispositionSchedule(recordCategory); + + // Create RM folder + recordFolder = rmService.createRecordFolder(recordCategory, "rmFolder"); + assertNotNull("Could not create rm folder", recordFolder); + recordFolder2 = rmService.createRecordFolder(recordCategory, "rmFolder2"); + assertNotNull("Could not create rm folder 2", recordFolder2); + } +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/CommonRMTestUtils.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/CommonRMTestUtils.java new file mode 100644 index 0000000000..202fed6b36 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/CommonRMTestUtils.java @@ -0,0 +1,215 @@ +/** + * + */ +package org.alfresco.module.org_alfresco_module_rm.test.util; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; +import org.alfresco.module.org_alfresco_module_rm.action.impl.FreezeAction; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.springframework.context.ApplicationContext; + +/** + * @author Roy Wetherall + */ +public class CommonRMTestUtils implements RecordsManagementModel +{ + private DispositionService dispositionService; + private NodeService nodeService; + private ContentService contentService; + private RecordsManagementActionService actionService; + + /** test values */ + public static final String DEFAULT_DISPOSITION_AUTHORITY = "disposition authority"; + public static final String DEFAULT_DISPOSITION_INSTRUCTIONS = "disposition instructions"; + public static final String DEFAULT_DISPOSITION_DESCRIPTION = "disposition action description"; + public static final String DEFAULT_EVENT_NAME = "case_closed"; + public static final String PERIOD_NONE = "none|0"; + public static final String PERIOD_IMMEDIATELY = "immediately|0"; + + public CommonRMTestUtils(ApplicationContext applicationContext) + { + dispositionService = (DispositionService)applicationContext.getBean("DispositionService"); + nodeService = (NodeService)applicationContext.getBean("NodeService"); + contentService = (ContentService)applicationContext.getBean("ContentService"); + actionService = (RecordsManagementActionService)applicationContext.getBean("RecordsManagementActionService"); + } + + /** + * + * @param container + * @return + */ + public DispositionSchedule createBasicDispositionSchedule(NodeRef container) + { + return createBasicDispositionSchedule(container, DEFAULT_DISPOSITION_INSTRUCTIONS, DEFAULT_DISPOSITION_AUTHORITY, false, true); + } + + /** + * + * @param container + * @param isRecordLevel + * @param defaultDispositionActions + * @return + */ + public DispositionSchedule createBasicDispositionSchedule( + NodeRef container, + String dispositionInstructions, + String dispositionAuthority, + boolean isRecordLevel, + boolean defaultDispositionActions) + { + Map dsProps = new HashMap(3); + dsProps.put(PROP_DISPOSITION_AUTHORITY, dispositionAuthority); + dsProps.put(PROP_DISPOSITION_INSTRUCTIONS, dispositionInstructions); + dsProps.put(PROP_RECORD_LEVEL_DISPOSITION, isRecordLevel); + DispositionSchedule dispositionSchedule = dispositionService.createDispositionSchedule(container, dsProps); + + if (defaultDispositionActions == true) + { + Map adParams = new HashMap(3); + adParams.put(PROP_DISPOSITION_ACTION_NAME, "cutoff"); + adParams.put(PROP_DISPOSITION_DESCRIPTION, DEFAULT_DISPOSITION_DESCRIPTION); + + List events = new ArrayList(1); + events.add(DEFAULT_EVENT_NAME); + adParams.put(PROP_DISPOSITION_EVENT, (Serializable)events); + + dispositionService.addDispositionActionDefinition(dispositionSchedule, adParams); + + adParams = new HashMap(3); + adParams.put(PROP_DISPOSITION_ACTION_NAME, "destroy"); + adParams.put(PROP_DISPOSITION_DESCRIPTION, DEFAULT_DISPOSITION_DESCRIPTION); + adParams.put(PROP_DISPOSITION_PERIOD, "immediately|0"); + + dispositionService.addDispositionActionDefinition(dispositionSchedule, adParams); + } + + return dispositionSchedule; + } + + public NodeRef createRecord(NodeRef recordFolder, String name) + { + return createRecord(recordFolder, name, null, "Some test content"); + } + + public NodeRef createRecord(NodeRef recordFolder, String name, String title) + { + Map props = new HashMap(1); + props.put(ContentModel.PROP_TITLE, title); + return createRecord(recordFolder, name, props, "Some test content"); + } + + public NodeRef createRecord(NodeRef recordFolder, String name, Map properties, String content) + { + // Create the document + if (properties == null) + { + properties = new HashMap(1); + } + if (properties.containsKey(ContentModel.PROP_NAME) == false) + { + properties.put(ContentModel.PROP_NAME, name); + } + NodeRef recordOne = nodeService.createNode(recordFolder, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name), + ContentModel.TYPE_CONTENT, + properties).getChildRef(); + + // Set the content + ContentWriter writer = contentService.getWriter(recordOne, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent(content); + + return recordOne; + } + + public void declareRecord(final NodeRef record) + { + AuthenticationUtil.runAs(new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + // Declare record + nodeService.setProperty(record, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + nodeService.setProperty(record, RecordsManagementModel.PROP_MEDIA_TYPE, "mediaTypeValue"); + nodeService.setProperty(record, RecordsManagementModel.PROP_FORMAT, "formatValue"); + nodeService.setProperty(record, RecordsManagementModel.PROP_DATE_RECEIVED, new Date()); + nodeService.setProperty(record, RecordsManagementModel.PROP_DATE_FILED, new Date()); + nodeService.setProperty(record, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + nodeService.setProperty(record, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + nodeService.setProperty(record, ContentModel.PROP_TITLE, "titleValue"); + actionService.executeRecordsManagementAction(record, "declareRecord"); + + return null; + } + + }, AuthenticationUtil.getAdminUserName()); + + } + + public void closeFolder(final NodeRef recordFolder) + { + AuthenticationUtil.runAs(new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + actionService.executeRecordsManagementAction(recordFolder, "closeRecordFolder"); + return null; + } + }, AuthenticationUtil.getAdminUserName()); + } + + public void freeze(final NodeRef nodeRef) + { + AuthenticationUtil.runAs(new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + Map params = new HashMap(1); + params.put(FreezeAction.PARAM_REASON, "Freeze reason."); + actionService.executeRecordsManagementAction(nodeRef, "freeze", params); + + return null; + } + + }, AuthenticationUtil.getSystemUserName()); + } + + public void unfreeze(final NodeRef nodeRef) + { + AuthenticationUtil.runAs(new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + actionService.executeRecordsManagementAction(nodeRef, "unfreeze"); + return null; + } + + }, AuthenticationUtil.getSystemUserName()); + } +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/TestAction.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/TestAction.java new file mode 100644 index 0000000000..aeb4ecccdc --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/TestAction.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.util; + +import java.io.Serializable; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.repository.NodeRef; + +public class TestAction extends RMActionExecuterAbstractBase +{ + public static final String NAME = "testAction"; + public static final String PARAM = "testActionParam"; + public static final String PARAM_VALUE = "value"; + + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + if (action.getParameterValue(PARAM).equals(PARAM_VALUE) == false) + { + throw new RuntimeException("Unexpected parameter value. Expected " + PARAM_VALUE + " actual " + action.getParameterValue(PARAM)); + } + this.nodeService.addAspect(actionedUponNodeRef, ASPECT_RECORD, null); + } + + @Override + public boolean isDispositionAction() + { + return true; + } + + @Override + protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + return true; + } + + +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/TestAction2.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/TestAction2.java new file mode 100644 index 0000000000..7143c78477 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/TestAction2.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.util; + +import java.io.Serializable; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.repository.NodeRef; + +public class TestAction2 extends RMActionExecuterAbstractBase +{ + public static final String NAME = "testAction2"; + + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + // Do nothing + } + + @Override + public boolean isDispositionAction() + { + return false; + } + + @Override + protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + return true; + } + + +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/TestActionParams.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/TestActionParams.java new file mode 100644 index 0000000000..8bcca2f956 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/TestActionParams.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.util; + +import java.io.Serializable; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.repository.NodeRef; + +public class TestActionParams extends RMActionExecuterAbstractBase +{ + public static final String NAME = "testActionParams"; + public static final String PARAM_DATE = "paramDate"; + + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + Object dateValue = action.getParameterValue(PARAM_DATE); + if ((dateValue instanceof java.util.Date) == false) + { + throw new AlfrescoRuntimeException("Param we not a Date as expected."); + } + } + + /* (non-Javadoc) + * @see org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase#isExecutableImpl(org.alfresco.service.cmr.repository.NodeRef, java.util.Map, boolean) + */ + @Override + protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + return true; + } +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/TestUtilities.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/TestUtilities.java new file mode 100644 index 0000000000..214e03d4a9 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/TestUtilities.java @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.util; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import junit.framework.Assert; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementSearchBehaviour; +import org.alfresco.module.org_alfresco_module_rm.script.BootstrapTestDataGet; +import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.view.ImporterBinding; +import org.alfresco.service.cmr.view.ImporterService; +import org.alfresco.service.cmr.view.Location; +import org.alfresco.service.namespace.QName; +import org.springframework.context.ApplicationContext; + +/** + * This class is an initial placeholder for miscellaneous helper methods used in + * the testing or test initialisation of the DOD5015 module. + * + * @author neilm + */ +public class TestUtilities implements RecordsManagementModel +{ + public static NodeRef loadFilePlanData(ApplicationContext applicationContext) + { + return TestUtilities.loadFilePlanData(applicationContext, true, false); + } + + public static final String TEST_FILE_PLAN_NAME = "testUtilities.filePlan"; + + private static NodeRef getFilePlan(NodeService nodeService, NodeRef rootNode) + { + NodeRef filePlan = null; + + // Try and find a file plan hanging from the root node + List assocs = nodeService.getChildAssocs(rootNode, ContentModel.ASSOC_CHILDREN, TYPE_FILE_PLAN); + if (assocs.size() != 0) + { + filePlan = assocs.get(0).getChildRef(); + } + + return filePlan; + } + + public static NodeRef loadFilePlanData(ApplicationContext applicationContext, boolean patchData, boolean alwaysLoad) + { + NodeService nodeService = (NodeService)applicationContext.getBean("NodeService"); + AuthorityService authorityService = (AuthorityService)applicationContext.getBean("AuthorityService"); + PermissionService permissionService = (PermissionService)applicationContext.getBean("PermissionService"); + SearchService searchService = (SearchService)applicationContext.getBean("SearchService"); + ImporterService importerService = (ImporterService)applicationContext.getBean("importerComponent"); + RecordsManagementService recordsManagementService = (RecordsManagementService)applicationContext.getBean("RecordsManagementService"); + RecordsManagementActionService recordsManagementActionService = (RecordsManagementActionService)applicationContext.getBean("RecordsManagementActionService"); + RecordsManagementSecurityService recordsManagementSecurityService = (RecordsManagementSecurityService)applicationContext.getBean("RecordsManagementSecurityService"); + RecordsManagementSearchBehaviour recordsManagementSearchBehaviour = (RecordsManagementSearchBehaviour)applicationContext.getBean("recordsManagementSearchBehaviour"); + DispositionService dispositionService = (DispositionService)applicationContext.getBean("DispositionService"); + + NodeRef filePlan = null; + NodeRef rootNode = nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); + + if (alwaysLoad == false && getFilePlan(nodeService, rootNode) != null) + { + return filePlan; + } + + // For now creating the filePlan beneath the + Map props = new HashMap(1); + props.put(ContentModel.PROP_NAME, TEST_FILE_PLAN_NAME); + filePlan = nodeService.createNode(rootNode, ContentModel.ASSOC_CHILDREN, + TYPE_FILE_PLAN, + TYPE_FILE_PLAN, + props).getChildRef(); + + // Do the data load into the the provided filePlan node reference + // TODO ... + InputStream is = TestUtilities.class.getClassLoader().getResourceAsStream( + "alfresco/module/org_alfresco_module_rm/dod5015/DODExampleFilePlan.xml"); + //"alfresco/module/org_alfresco_module_rm/bootstrap/temp.xml"); + Assert.assertNotNull("The DODExampleFilePlan.xml import file could not be found", is); + Reader viewReader = new InputStreamReader(is); + Location location = new Location(filePlan); + importerService.importView(viewReader, location, REPLACE_BINDING, null); + + if (patchData == true) + { + // Tempory call out to patch data after AMP + BootstrapTestDataGet.patchLoadedData(searchService, nodeService, recordsManagementService, + recordsManagementActionService, permissionService, + authorityService, recordsManagementSecurityService, + recordsManagementSearchBehaviour, + dispositionService); + } + + return filePlan; + } + + public static NodeRef getRecordSeries(RecordsManagementService rmService, NodeService nodeService, String seriesName) + { + NodeRef result = null; + NodeRef rootNode = nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); + NodeRef filePlan = getFilePlan(nodeService, rootNode); + + if (filePlan != null) + { + result = nodeService.getChildByName(filePlan, ContentModel.ASSOC_CONTAINS, seriesName); + } + return result; + } + + public static NodeRef getRecordCategory(RecordsManagementService rmService, NodeService nodeService, String seriesName, String categoryName) + { + NodeRef seriesNodeRef = getRecordSeries(rmService, nodeService, seriesName); + + NodeRef result = null; + if (seriesNodeRef != null) + { + result = nodeService.getChildByName(seriesNodeRef, ContentModel.ASSOC_CONTAINS, categoryName); + } + return result; + } + + public static NodeRef getRecordFolder(RecordsManagementService rmService, NodeService nodeService, String seriesName, String categoryName, String folderName) + { + NodeRef categoryNodeRef = getRecordCategory(rmService, nodeService, seriesName, categoryName); + + NodeRef result = null; + if (categoryNodeRef != null) + { + result = nodeService.getChildByName(categoryNodeRef, ContentModel.ASSOC_CONTAINS, folderName); + } + return result; + + } + + + // TODO .. do we need to redeclare this here ?? + private static ImporterBinding REPLACE_BINDING = new ImporterBinding() + { + + public UUID_BINDING getUUIDBinding() + { + return UUID_BINDING.REPLACE_EXISTING; + } + + public String getValue(String key) + { + return null; + } + + public boolean allowReferenceWithinTransaction() + { + return false; + } + + public QName[] getExcludedClasses() + { + return null; + } + + }; + + public static void declareRecord(NodeRef recordToDeclare, NodeService nodeService, + RecordsManagementActionService rmActionService) + { + // Declare record + Map propValues = nodeService.getProperties(recordToDeclare); + propValues.put(RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); + List smList = new ArrayList(2); +// smList.add(DOD5015Test.FOUO); +// smList.add(DOD5015Test.NOFORN); + propValues.put(RecordsManagementModel.PROP_SUPPLEMENTAL_MARKING_LIST, (Serializable)smList); + propValues.put(RecordsManagementModel.PROP_MEDIA_TYPE, "mediaTypeValue"); + propValues.put(RecordsManagementModel.PROP_FORMAT, "formatValue"); + propValues.put(RecordsManagementModel.PROP_DATE_RECEIVED, new Date()); + propValues.put(RecordsManagementModel.PROP_ORIGINATOR, "origValue"); + propValues.put(RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); + propValues.put(ContentModel.PROP_TITLE, "titleValue"); + nodeService.setProperties(recordToDeclare, propValues); + rmActionService.executeRecordsManagementAction(recordToDeclare, "declareRecord"); + } +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/TestWebScriptRepoServer.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/TestWebScriptRepoServer.java new file mode 100644 index 0000000000..57b1748332 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/TestWebScriptRepoServer.java @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.util; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +import org.alfresco.repo.security.authentication.AuthenticationException; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.security.AuthenticationService; +import org.alfresco.util.EqualsHelper; +import org.springframework.extensions.webscripts.TestWebScriptServer; +import org.springframework.context.support.ClassPathXmlApplicationContext; + + +/** + * Stand-alone Web Script Test Server + * + * @author davidc + */ +public class TestWebScriptRepoServer extends TestWebScriptServer +{ + /** + * Main entry point. + */ + public static void main(String[] args) + { + try + { + TestWebScriptServer testServer = getTestServer(); + AuthenticationUtil.setRunAsUserSystem(); + testServer.rep(); + } + catch(Throwable e) + { + StringWriter strWriter = new StringWriter(); + PrintWriter printWriter = new PrintWriter(strWriter); + e.printStackTrace(printWriter); + System.out.println(strWriter.toString()); + } + finally + { + System.exit(0); + } + } + + private final static String[] CONFIG_LOCATIONS = new String[] + { + "classpath:alfresco/application-context.xml", + "classpath:alfresco/web-scripts-application-context.xml", + "classpath:alfresco/web-scripts-application-context-test.xml" + }; + + /** A static reference to the application context being used */ + private static ClassPathXmlApplicationContext ctx; + private static String appendedTestConfiguration; + + private RetryingTransactionHelper retryingTransactionHelper; + private AuthenticationService authenticationService; + + + /** + * Sets helper that provides transaction callbacks + */ + public void setTransactionHelper(RetryingTransactionHelper retryingTransactionHelper) + { + this.retryingTransactionHelper = retryingTransactionHelper; + } + + /** + * @param authenticationService + */ + public void setAuthenticationService(AuthenticationService authenticationService) + { + this.authenticationService = authenticationService; + } + + /** + * Get default user name + */ + protected String getDefaultUserName() + { + return AuthenticationUtil.getAdminUserName(); + } + + /** + * {@inheritDoc #getTestServer(String)} + */ + public static TestWebScriptServer getTestServer() + { + return getTestServer(null); + } + + /** + * Start up a context and get the server bean. + *

+ * This method will close and restart the application context only if the configuration has + * changed. + * + * @param appendTestConfigLocation additional context file to include in the application context + * @return Test Server + */ + public static synchronized TestWebScriptServer getTestServer(String appendTestConfigLocation) + { + if (TestWebScriptRepoServer.ctx != null) + { + boolean configChanged = !EqualsHelper.nullSafeEquals( + appendTestConfigLocation, + TestWebScriptRepoServer.appendedTestConfiguration); + if (configChanged) + { + // The config changed, so close the context (it'll be restarted later) + try + { + ctx.close(); + ctx = null; + } + catch (Throwable e) + { + throw new RuntimeException("Failed to shut down existing application context", e); + } + } + else + { + // There is already a context with the required configuration + } + } + + // Check if we need to start/restart the context + if (TestWebScriptRepoServer.ctx == null) + { + // Restart it + final String[] configLocations; + if (appendTestConfigLocation == null) + { + configLocations = CONFIG_LOCATIONS; + } + else + { + configLocations = new String[CONFIG_LOCATIONS.length+1]; + System.arraycopy(CONFIG_LOCATIONS, 0, configLocations, 0, CONFIG_LOCATIONS.length); + configLocations[CONFIG_LOCATIONS.length] = appendTestConfigLocation; + } + TestWebScriptRepoServer.ctx = new ClassPathXmlApplicationContext(configLocations); + TestWebScriptRepoServer.appendedTestConfiguration = appendTestConfigLocation; + } + + // Get the bean + TestWebScriptServer testServer = (org.alfresco.repo.web.scripts.TestWebScriptRepoServer)TestWebScriptRepoServer.ctx.getBean("webscripts.test"); + return testServer; + } + + /** + * Interpret a single command using the BufferedReader passed in for any data needed. + * + * @param line The unparsed command + * @return The textual output of the command. + */ + @Override + protected String interpretCommand(final String line) + throws IOException + { + try + { + if (username.startsWith("TICKET_")) + { + try + { + retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback() + { + public Object execute() throws Exception + { + authenticationService.validate(username); + return null; + } + }); + return executeCommand(line); + } + finally + { + authenticationService.clearCurrentSecurityContext(); + } + } + } + catch(AuthenticationException e) + { + executeCommand("user " + getDefaultUserName()); + } + + // execute command in context of currently selected user + return AuthenticationUtil.runAs(new RunAsWork() + { + @SuppressWarnings("synthetic-access") + public String doWork() throws Exception + { + return executeCommand(line); + } + }, username); + } + +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/webscript/BootstraptestDataRestApiTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/webscript/BootstraptestDataRestApiTest.java new file mode 100644 index 0000000000..e137b85263 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/webscript/BootstraptestDataRestApiTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.webscript; + +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMWebScriptTestCase; +import org.alfresco.module.org_alfresco_module_rm.test.util.TestUtilities; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.web.scripts.BaseWebScriptTest; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest; + +/** + * This class tests the Rest API for disposition related operations + * + * @author Roy Wetherall + */ +public class BootstraptestDataRestApiTest extends BaseRMWebScriptTestCase implements RecordsManagementModel +{ + protected static StoreRef SPACES_STORE = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore"); + protected static final String URL = "/api/rma/bootstraptestdata"; + protected static final String SERVICE_URL_PREFIX = "/alfresco/service"; + protected static final String APPLICATION_JSON = "application/json"; + + protected NodeService nodeService; + protected RetryingTransactionHelper transactionHelper; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + nodeService = (NodeService) getServer().getApplicationContext().getBean("NodeService"); + transactionHelper = (RetryingTransactionHelper)getServer().getApplicationContext().getBean("retryingTransactionHelper"); + } + + public void testBoostrapTestData() throws Exception + { + final NodeRef filePlan = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + return TestUtilities.loadFilePlanData(getServer().getApplicationContext(), false, true); + } + }); + + sendRequest(new GetRequest(URL), 200); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + nodeService.deleteNode(filePlan); + return null; + } + }); + } + +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/webscript/DispositionRestApiTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/webscript/DispositionRestApiTest.java new file mode 100644 index 0000000000..7a6521d4ed --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/webscript/DispositionRestApiTest.java @@ -0,0 +1,438 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.webscript; + +import java.text.MessageFormat; + +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMWebScriptTestCase; +import org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.Period; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.util.GUID; +import org.json.JSONArray; +import org.json.JSONObject; +import org.json.JSONTokener; +import org.springframework.extensions.webscripts.TestWebScriptServer.DeleteRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.PostRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.PutRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.Response; + +/** + * This class tests the Rest API for disposition related operations + * + * @author Gavin Cornwell + */ +public class DispositionRestApiTest extends BaseRMWebScriptTestCase implements RecordsManagementModel +{ + protected static StoreRef SPACES_STORE = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore"); + protected static final String GET_SCHEDULE_URL_FORMAT = "/api/node/{0}/dispositionschedule"; + protected static final String GET_LIFECYCLE_URL_FORMAT = "/api/node/{0}/nextdispositionaction"; + protected static final String POST_ACTIONDEF_URL_FORMAT = "/api/node/{0}/dispositionschedule/dispositionactiondefinitions"; + protected static final String DELETE_ACTIONDEF_URL_FORMAT = "/api/node/{0}/dispositionschedule/dispositionactiondefinitions/{1}"; + protected static final String PUT_ACTIONDEF_URL_FORMAT = "/api/node/{0}/dispositionschedule/dispositionactiondefinitions/{1}"; + protected static final String GET_LIST_URL = "/api/rma/admin/listofvalues"; + protected static final String SERVICE_URL_PREFIX = "/alfresco/service"; + protected static final String APPLICATION_JSON = "application/json"; + + + public void testGetDispositionSchedule() throws Exception + { + // Test 404 status for non existent node + int expectedStatus = 404; + String nonExistentNode = "workspace/SpacesStore/09ca1e02-1c87-4a53-97e7-xxxxxxxxxxxx"; + String nonExistentUrl = MessageFormat.format(GET_SCHEDULE_URL_FORMAT, nonExistentNode); + Response rsp = sendRequest(new GetRequest(nonExistentUrl), expectedStatus); + + // Test 404 status for node that doesn't have dispostion schedule i.e. a record series + String seriesNodeUrl = recordSeries.toString().replace("://", "/"); + String wrongNodeUrl = MessageFormat.format(GET_SCHEDULE_URL_FORMAT, seriesNodeUrl); + rsp = sendRequest(new GetRequest(wrongNodeUrl), expectedStatus); + + // Test data structure returned from "AIS Audit Records" + expectedStatus = 200; + + String categoryNodeUrl = recordCategory.toString().replace("://", "/"); + String requestUrl = MessageFormat.format(GET_SCHEDULE_URL_FORMAT, categoryNodeUrl); + rsp = sendRequest(new GetRequest(requestUrl), expectedStatus); + assertEquals("application/json;charset=UTF-8", rsp.getContentType()); + + // get response as JSON + JSONObject jsonParsedObject = new JSONObject(new JSONTokener(rsp.getContentAsString())); + assertNotNull(jsonParsedObject); + + // check JSON data + JSONObject dataObj = jsonParsedObject.getJSONObject("data"); + assertNotNull(dataObj); + JSONObject rootDataObject = (JSONObject)dataObj; + assertEquals(10, rootDataObject.length()); + + // check individual data items + String serviceUrl = SERVICE_URL_PREFIX + requestUrl; + String url = rootDataObject.getString("url"); + assertEquals(serviceUrl, url); + + String authority = rootDataObject.getString("authority"); + + assertEquals(CommonRMTestUtils.DEFAULT_DISPOSITION_AUTHORITY, authority); + + String instructions = rootDataObject.getString("instructions"); + assertEquals(CommonRMTestUtils.DEFAULT_DISPOSITION_INSTRUCTIONS, instructions); + + String actionsUrl = rootDataObject.getString("actionsUrl"); + assertEquals(serviceUrl + "/dispositionactiondefinitions", actionsUrl); + + boolean recordLevel = rootDataObject.getBoolean("recordLevelDisposition"); + assertFalse(recordLevel); + + assertFalse(rootDataObject.getBoolean("canStepsBeRemoved")); + + JSONArray actions = rootDataObject.getJSONArray("actions"); + assertNotNull(actions); + assertEquals(2, actions.length()); + JSONObject action1 = (JSONObject)actions.get(0); + assertEquals(9, action1.length()); + assertNotNull(action1.get("id")); + assertNotNull(action1.get("url")); + assertEquals(0, action1.getInt("index")); + assertEquals("cutoff", action1.getString("name")); + assertEquals("Cutoff", action1.getString("label")); + assertTrue(action1.getBoolean("eligibleOnFirstCompleteEvent")); + + JSONObject action2 = (JSONObject)actions.get(1); + assertEquals(8, action2.length()); + + // make sure the disposition schedule node ref is present and valid + String scheduleNodeRefJSON = rootDataObject.getString("nodeRef"); + NodeRef scheduleNodeRef = new NodeRef(scheduleNodeRefJSON); + assertTrue(this.nodeService.exists(scheduleNodeRef)); + + // create a new recordCategory node in the recordSeries and then get + // the disposition schedule + NodeRef newRecordCategory = rmService.createRecordCategory(recordSeries, GUID.generate()); + dispositionService.createDispositionSchedule(newRecordCategory, null); + + categoryNodeUrl = newRecordCategory.toString().replace("://", "/"); + requestUrl = MessageFormat.format(GET_SCHEDULE_URL_FORMAT, categoryNodeUrl); + //System.out.println("GET response: " + rsp.getContentAsString()); + rsp = sendRequest(new GetRequest(requestUrl), expectedStatus); + + // get response as JSON + jsonParsedObject = new JSONObject(new JSONTokener(rsp.getContentAsString())); + System.out.println(rsp.getContentAsString()); + assertNotNull(jsonParsedObject); + + // check JSON data + dataObj = jsonParsedObject.getJSONObject("data"); + assertNotNull(dataObj); + rootDataObject = (JSONObject)dataObj; + assertEquals(8, rootDataObject.length()); + actions = rootDataObject.getJSONArray("actions"); + assertNotNull(actions); + assertEquals(0, actions.length()); + } + + public void testPostDispositionAction() throws Exception + { + // create a new recordCategory node in the recordSeries and then get + // the disposition schedule + NodeRef newRecordCategory = rmService.createRecordCategory(recordSeries, GUID.generate()); + dispositionService.createDispositionSchedule(newRecordCategory, null); + + String categoryNodeUrl = newRecordCategory.toString().replace("://", "/"); + String requestUrl = MessageFormat.format(POST_ACTIONDEF_URL_FORMAT, categoryNodeUrl); + + // Construct the JSON request. + String name = "destroy"; + String desc = "Destroy this record after 5 years"; + String period = "year|5"; + String periodProperty = "rma:cutOffDate"; + boolean eligibleOnFirstCompleteEvent = true; + + JSONObject jsonPostData = new JSONObject(); + jsonPostData.put("name", name); + jsonPostData.put("description", desc); + jsonPostData.put("period", period); + jsonPostData.put("location", "my location"); + jsonPostData.put("periodProperty", periodProperty); + jsonPostData.put("eligibleOnFirstCompleteEvent", eligibleOnFirstCompleteEvent); + JSONArray events = new JSONArray(); + events.put("superseded"); + events.put("no_longer_needed"); + jsonPostData.put("events", events); + + // Submit the JSON request. + String jsonPostString = jsonPostData.toString(); + Response rsp = sendRequest(new PostRequest(requestUrl, jsonPostString, APPLICATION_JSON), 200); + + // check the returned data is what was expected + JSONObject jsonResponse = new JSONObject(new JSONTokener(rsp.getContentAsString())); + JSONObject dataObj = jsonResponse.getJSONObject("data"); + JSONObject rootDataObject = (JSONObject)dataObj; + assertNotNull(rootDataObject.getString("id")); + assertNotNull(rootDataObject.getString("url")); + assertEquals(0, rootDataObject.getInt("index")); + assertEquals(name, rootDataObject.getString("name")); + assertEquals("Destroy", rootDataObject.getString("label")); + assertEquals(desc, rootDataObject.getString("description")); + assertEquals(period, rootDataObject.getString("period")); + assertEquals("my location", rootDataObject.getString("location")); + assertEquals(periodProperty, rootDataObject.getString("periodProperty")); + assertTrue(rootDataObject.getBoolean("eligibleOnFirstCompleteEvent")); + events = rootDataObject.getJSONArray("events"); + assertNotNull(events); + assertEquals(2, events.length()); + assertEquals("superseded", events.get(0)); + assertEquals("no_longer_needed", events.get(1)); + + // test the minimum amount of data required to create an action definition + jsonPostData = new JSONObject(); + jsonPostData.put("name", name); + jsonPostString = jsonPostData.toString(); + rsp = sendRequest(new PostRequest(requestUrl, jsonPostString, APPLICATION_JSON), 200); + + // check the returned data is what was expected + jsonResponse = new JSONObject(new JSONTokener(rsp.getContentAsString())); + dataObj = jsonResponse.getJSONObject("data"); + assertNotNull(rootDataObject.getString("id")); + assertNotNull(rootDataObject.getString("url")); + assertEquals(0, rootDataObject.getInt("index")); + assertEquals(name, dataObj.getString("name")); + assertEquals("none|0", dataObj.getString("period")); + assertFalse(dataObj.has("description")); + assertFalse(dataObj.has("periodProperty")); + assertFalse(dataObj.has("events")); + assertTrue(dataObj.getBoolean("eligibleOnFirstCompleteEvent")); + + // negative test to ensure not supplying mandatory data results in an error + jsonPostData = new JSONObject(); + jsonPostData.put("description", desc); + jsonPostData.put("period", period); + jsonPostString = jsonPostData.toString(); + sendRequest(new PostRequest(requestUrl, jsonPostString, APPLICATION_JSON), 400); + } + + public void testPutDispositionAction() throws Exception + { + NodeRef newRecordCategory = rmService.createRecordCategory(recordSeries, GUID.generate()); + dispositionService.createDispositionSchedule(newRecordCategory, null); + + // create an action definition to then update + String categoryNodeUrl = newRecordCategory.toString().replace("://", "/"); + String postRequestUrl = MessageFormat.format(POST_ACTIONDEF_URL_FORMAT, categoryNodeUrl); + JSONObject jsonPostData = new JSONObject(); + jsonPostData.put("name", "cutoff"); + String jsonPostString = jsonPostData.toString(); + sendRequest(new PostRequest(postRequestUrl, jsonPostString, APPLICATION_JSON), 200); + + // verify the action definition is present and retrieve it's id + String getRequestUrl = MessageFormat.format(GET_SCHEDULE_URL_FORMAT, categoryNodeUrl); + Response rsp = sendRequest(new GetRequest(getRequestUrl), 200); + JSONObject json = new JSONObject(new JSONTokener(rsp.getContentAsString())); + JSONObject actionDef = json.getJSONObject("data").getJSONArray("actions").getJSONObject(0); + String actionDefId = actionDef.getString("id"); + assertEquals("cutoff", actionDef.getString("name")); + assertEquals("Cutoff", actionDef.getString("label")); + assertEquals("none|0", actionDef.getString("period")); + assertFalse(actionDef.has("description")); + assertFalse(actionDef.has("events")); + + // define body for PUT request + String name = "destroy"; + String desc = "Destroy this record after 5 years"; + String period = "year|5"; + String location = "my location"; + String periodProperty = "rma:cutOffDate"; + boolean eligibleOnFirstCompleteEvent = false; + + jsonPostData = new JSONObject(); + jsonPostData.put("name", name); + jsonPostData.put("description", desc); + jsonPostData.put("period", period); + jsonPostData.put("location", location); + jsonPostData.put("periodProperty", periodProperty); + jsonPostData.put("eligibleOnFirstCompleteEvent", eligibleOnFirstCompleteEvent); + JSONArray events = new JSONArray(); + events.put("superseded"); + events.put("no_longer_needed"); + jsonPostData.put("events", events); + jsonPostString = jsonPostData.toString(); + + // try and update a non existent action definition to check for 404 + String putRequestUrl = MessageFormat.format(PUT_ACTIONDEF_URL_FORMAT, categoryNodeUrl, "xyz"); + rsp = sendRequest(new PutRequest(putRequestUrl, jsonPostString, APPLICATION_JSON), 404); + + // update the action definition + putRequestUrl = MessageFormat.format(PUT_ACTIONDEF_URL_FORMAT, categoryNodeUrl, actionDefId); + rsp = sendRequest(new PutRequest(putRequestUrl, jsonPostString, APPLICATION_JSON), 200); + + // check the update happened correctly + json = new JSONObject(new JSONTokener(rsp.getContentAsString())); + actionDef = json.getJSONObject("data"); + assertEquals(name, actionDef.getString("name")); + assertEquals("Destroy", actionDef.getString("label")); + assertEquals(desc, actionDef.getString("description")); + assertEquals(period, actionDef.getString("period")); + assertEquals(location, actionDef.getString("location")); + assertEquals(periodProperty, actionDef.getString("periodProperty")); + assertFalse(actionDef.getBoolean("eligibleOnFirstCompleteEvent")); + assertEquals(2, actionDef.getJSONArray("events").length()); + assertEquals("superseded", actionDef.getJSONArray("events").getString(0)); + assertEquals("no_longer_needed", actionDef.getJSONArray("events").getString(1)); + } + + public void testDeleteDispositionAction() throws Exception + { + NodeRef newRecordCategory = rmService.createRecordCategory(recordSeries, GUID.generate()); + dispositionService.createDispositionSchedule(newRecordCategory, null); + + // create an action definition to then delete + String categoryNodeUrl = newRecordCategory.toString().replace("://", "/"); + String postRequestUrl = MessageFormat.format(POST_ACTIONDEF_URL_FORMAT, categoryNodeUrl); + JSONObject jsonPostData = new JSONObject(); + jsonPostData.put("name", "cutoff"); + String jsonPostString = jsonPostData.toString(); + sendRequest(new PostRequest(postRequestUrl, jsonPostString, APPLICATION_JSON), 200); + + // verify the action definition is present and retrieve it's id + String getRequestUrl = MessageFormat.format(GET_SCHEDULE_URL_FORMAT, categoryNodeUrl); + Response rsp = sendRequest(new GetRequest(getRequestUrl), 200); + JSONObject json = new JSONObject(new JSONTokener(rsp.getContentAsString())); + String actionDefId = json.getJSONObject("data").getJSONArray("actions").getJSONObject(0).getString("id"); + + // try and delete a non existent action definition to check for 404 + String deleteRequestUrl = MessageFormat.format(DELETE_ACTIONDEF_URL_FORMAT, categoryNodeUrl, "xyz"); + rsp = sendRequest(new DeleteRequest(deleteRequestUrl), 404); + + // now delete the action defintion created above + deleteRequestUrl = MessageFormat.format(DELETE_ACTIONDEF_URL_FORMAT, categoryNodeUrl, actionDefId); + rsp = sendRequest(new DeleteRequest(deleteRequestUrl), 200); + + // verify it got deleted + getRequestUrl = MessageFormat.format(GET_SCHEDULE_URL_FORMAT, categoryNodeUrl); + rsp = sendRequest(new GetRequest(getRequestUrl), 200); + json = new JSONObject(new JSONTokener(rsp.getContentAsString())); + JSONArray actions = json.getJSONObject("data").getJSONArray("actions"); + assertEquals(0, actions.length()); + } + + public void testGetDispositionLifecycle() throws Exception + { + // Test 404 for disposition lifecycle request on incorrect node + String categoryUrl = recordCategory.toString().replace("://", "/"); + String requestUrl = MessageFormat.format(GET_LIFECYCLE_URL_FORMAT, categoryUrl); + Response rsp = sendRequest(new GetRequest(requestUrl), 404); + + NodeRef newRecordFolder = rmService.createRecordFolder(recordCategory, "recordFolder"); + + + // there should now be a disposition lifecycle for the record + requestUrl = MessageFormat.format(GET_LIFECYCLE_URL_FORMAT, newRecordFolder.toString().replace("://", "/")); + rsp = sendRequest(new GetRequest(requestUrl), 200); + System.out.println("GET : " + rsp.getContentAsString()); + assertEquals("application/json;charset=UTF-8", rsp.getContentType()); + + // get response as JSON + JSONObject jsonParsedObject = new JSONObject(new JSONTokener(rsp.getContentAsString())); + assertNotNull(jsonParsedObject); + + // check mandatory stuff is present + JSONObject dataObj = jsonParsedObject.getJSONObject("data"); + assertEquals(SERVICE_URL_PREFIX + requestUrl, dataObj.getString("url")); + assertEquals("cutoff", dataObj.getString("name")); + assertEquals("Cutoff", dataObj.getString("label")); + assertFalse(dataObj.getBoolean("eventsEligible")); + assertTrue(dataObj.has("events")); + JSONArray events = dataObj.getJSONArray("events"); + assertEquals(1, events.length()); + JSONObject event1 = events.getJSONObject(0); + assertEquals("case_closed", event1.get("name")); + assertEquals("Case Closed", event1.get("label")); + assertFalse(event1.getBoolean("complete")); + assertFalse(event1.getBoolean("automatic")); + + // check stuff expected to be missing is missing + assertFalse(dataObj.has("asOf")); + assertFalse(dataObj.has("startedAt")); + assertFalse(dataObj.has("startedBy")); + assertFalse(dataObj.has("completedAt")); + assertFalse(dataObj.has("completedBy")); + assertFalse(event1.has("completedAt")); + assertFalse(event1.has("completedBy")); + } + + public void testGetListOfValues() throws Exception + { + // call the list service + Response rsp = sendRequest(new GetRequest(GET_LIST_URL), 200); + assertEquals("application/json;charset=UTF-8", rsp.getContentType()); + + // get response as JSON + JSONObject jsonParsedObject = new JSONObject(new JSONTokener(rsp.getContentAsString())); + assertNotNull(jsonParsedObject); + JSONObject data = jsonParsedObject.getJSONObject("data"); + + // check dispostion actions + JSONObject actions = data.getJSONObject("dispositionActions"); + assertEquals(SERVICE_URL_PREFIX + GET_LIST_URL + "/dispositionactions", actions.getString("url")); + JSONArray items = actions.getJSONArray("items"); + assertEquals(actionService.getDispositionActions().size(), items.length()); + assertTrue(items.length() > 0); + JSONObject item = items.getJSONObject(0); + assertTrue(item.length() == 2); + assertTrue(item.has("label")); + assertTrue(item.has("value")); + + // check events + JSONObject events = data.getJSONObject("events"); + assertEquals(SERVICE_URL_PREFIX + GET_LIST_URL + "/events", events.getString("url")); + items = events.getJSONArray("items"); + assertEquals(eventService.getEvents().size(), items.length()); + assertTrue(items.length() > 0); + item = items.getJSONObject(0); + assertTrue(item.length() == 3); + assertTrue(item.has("label")); + assertTrue(item.has("value")); + assertTrue(item.has("automatic")); + + // check period types + JSONObject periodTypes = data.getJSONObject("periodTypes"); + assertEquals(SERVICE_URL_PREFIX + GET_LIST_URL + "/periodtypes", periodTypes.getString("url")); + items = periodTypes.getJSONArray("items"); + assertEquals(Period.getProviderNames().size()-1, items.length()); + assertTrue(items.length() > 0); + item = items.getJSONObject(0); + assertTrue(item.length() == 2); + assertTrue(item.has("label")); + assertTrue(item.has("value")); + + // check period properties + JSONObject periodProperties = data.getJSONObject("periodProperties"); + assertEquals(SERVICE_URL_PREFIX + GET_LIST_URL + "/periodproperties", periodProperties.getString("url")); + items = periodProperties.getJSONArray("items"); + assertEquals(4, items.length()); + assertTrue(items.length() > 0); + item = items.getJSONObject(0); + assertTrue(item.length() == 2); + assertTrue(item.has("label")); + assertTrue(item.has("value")); + } +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/webscript/EmailMapScriptTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/webscript/EmailMapScriptTest.java new file mode 100644 index 0000000000..1187aebf9b --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/webscript/EmailMapScriptTest.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.webscript; + +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMWebScriptTestCase; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.security.AuthenticationService; +import org.json.JSONArray; +import org.json.JSONObject; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.PostRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.Response; + +public class EmailMapScriptTest extends BaseRMWebScriptTestCase +{ + + public final static String URL_RM_EMAILMAP = "/api/rma/admin/emailmap"; + + AuthenticationService authenticationService; + + @Override + protected void setUp() throws Exception + { + this.authenticationService = (AuthenticationService)getServer().getApplicationContext().getBean("AuthenticationService"); +// setCurrentUser(AuthenticationUtil.getAdminUserName()); + super.setUp(); + + // Set the current security context as admin + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + + } + + @Override + protected void tearDown() throws Exception + { + super.tearDown(); + + } + + public void testGetEmailMap() throws Exception + { + { + Response response = sendRequest(new GetRequest(URL_RM_EMAILMAP), Status.STATUS_OK); + + @SuppressWarnings("unused") + JSONObject top = new JSONObject(response.getContentAsString()); + System.out.println(response.getContentAsString()); + //JSONArray data = top.getJSONArray("data"); + } + } + + public void testUpdateEmailMap() throws Exception + { + /** + * Update the list by adding two values + */ + { + JSONObject obj = new JSONObject(); + + JSONArray add = new JSONArray(); + JSONObject val = new JSONObject(); + val.put("from", "whatever"); + val.put("to", "rmc:Wibble"); + add.put(val); + JSONObject val2 = new JSONObject(); + val2.put("from", "whatever"); + val2.put("to", "rmc:wobble"); + add.put(val2); + + obj.put("add", add); + + System.out.println(obj.toString()); + + /** + * Now do a post to add a couple of values + */ + Response response = sendRequest(new PostRequest(URL_RM_EMAILMAP, obj.toString(), "application/json"), Status.STATUS_OK); + System.out.println(response.getContentAsString()); + // Check the response + + + JSONArray delete = new JSONArray(); + delete.put(val2); + + } + + /** + * Update the list by deleting a value + * + * "whatever" has two mappings, delete one of them + */ + { + JSONObject obj = new JSONObject(); + JSONObject val2 = new JSONObject(); + JSONArray delete = new JSONArray(); + val2.put("from", "whatever"); + val2.put("to", "rmc:wobble"); + delete.put(val2); + obj.put("delete", delete); + + /** + * Now do a post to delete a couple of values + */ + Response response = sendRequest(new PostRequest(URL_RM_EMAILMAP, obj.toString(), "application/json"), Status.STATUS_OK); + System.out.println(response.getContentAsString()); + + JSONObject top = new JSONObject(response.getContentAsString()); + JSONObject data = top.getJSONObject("data"); + JSONArray mappings = data.getJSONArray("mappings"); + + boolean wibbleFound = false; + for(int i = 0; i < mappings.length(); i++) + { + JSONObject mapping = mappings.getJSONObject(i); + + + if(mapping.get("from").equals("whatever")) + { + if(mapping.get("to").equals("rmc:Wibble")) + { + wibbleFound = true; + } + else + { + fail("custom mapping for field not deleted"); + } + } + } + assertTrue(wibbleFound); + } + } +} + diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/webscript/EventRestApiTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/webscript/EventRestApiTest.java new file mode 100644 index 0000000000..8b8229278c --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/webscript/EventRestApiTest.java @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.webscript; + +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMWebScriptTestCase; +import org.alfresco.util.GUID; +import org.json.JSONObject; +import org.springframework.extensions.webscripts.TestWebScriptServer.DeleteRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.PostRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.PutRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.Response; + +/** + * RM event REST API test + * + * @author Roy Wetherall + */ +public class EventRestApiTest extends BaseRMWebScriptTestCase implements RecordsManagementModel +{ + protected static final String GET_EVENTS_URL = "/api/rma/admin/rmevents"; + protected static final String GET_EVENTTYPES_URL = "/api/rma/admin/rmeventtypes"; + protected static final String SERVICE_URL_PREFIX = "/alfresco/service"; + protected static final String APPLICATION_JSON = "application/json"; + + protected static final String DISPLAY_LABEL = "display label"; + protected static final String EVENT_TYPE = "rmEventType.simple"; + protected static final String KEY_EVENT_NAME = "eventName"; + protected static final String KEY_EVENT_TYPE = "eventType"; + protected static final String KEY_EVENT_DISPLAY_LABEL = "eventDisplayLabel"; + + public void testGetEventTypes() throws Exception + { + Response rsp = sendRequest(new GetRequest(GET_EVENTTYPES_URL),200); + String rspContent = rsp.getContentAsString(); + + JSONObject obj = new JSONObject(rspContent); + JSONObject types = obj.getJSONObject("data"); + assertNotNull(types); + + JSONObject type = types.getJSONObject("rmEventType.simple"); + assertNotNull(type); + assertEquals("rmEventType.simple", type.getString("eventTypeName")); + assertNotNull(type.getString("eventTypeDisplayLabel")); + + System.out.println(rspContent); + } + + public void testGetEvents() throws Exception + { + String event1 = GUID.generate(); + String event2 = GUID.generate(); + + // Create a couple or events by hand + eventService.addEvent(EVENT_TYPE, event1, DISPLAY_LABEL); + eventService.addEvent(EVENT_TYPE, event2, DISPLAY_LABEL); + + try + { + // Get the events + Response rsp = sendRequest(new GetRequest(GET_EVENTS_URL),200); + String rspContent = rsp.getContentAsString(); + + JSONObject obj = new JSONObject(rspContent); + JSONObject roles = obj.getJSONObject("data"); + assertNotNull(roles); + + JSONObject eventObj = roles.getJSONObject(event1); + assertNotNull(eventObj); + assertEquals(event1, eventObj.get(KEY_EVENT_NAME)); + assertEquals(DISPLAY_LABEL, eventObj.get(KEY_EVENT_DISPLAY_LABEL)); + assertEquals(EVENT_TYPE, eventObj.get(KEY_EVENT_TYPE)); + + eventObj = roles.getJSONObject(event2); + assertNotNull(eventObj); + assertEquals(event2, eventObj.get(KEY_EVENT_NAME)); + assertEquals(DISPLAY_LABEL, eventObj.get(KEY_EVENT_DISPLAY_LABEL)); + assertEquals(EVENT_TYPE, eventObj.get(KEY_EVENT_TYPE)); + } + finally + { + // Clean up + eventService.removeEvent(event1); + eventService.removeEvent(event2); + } + + } + + public void testPostEvents() throws Exception + { + String eventName= GUID.generate(); + + JSONObject obj = new JSONObject(); + obj.put(KEY_EVENT_NAME, eventName); + obj.put(KEY_EVENT_DISPLAY_LABEL, DISPLAY_LABEL); + obj.put(KEY_EVENT_TYPE, EVENT_TYPE); + + Response rsp = sendRequest(new PostRequest(GET_EVENTS_URL, obj.toString(), APPLICATION_JSON),200); + try + { + String rspContent = rsp.getContentAsString(); + + JSONObject resultObj = new JSONObject(rspContent); + JSONObject eventObj = resultObj.getJSONObject("data"); + assertNotNull(eventObj); + + assertEquals(eventName, eventObj.get(KEY_EVENT_NAME)); + assertEquals(DISPLAY_LABEL, eventObj.get(KEY_EVENT_DISPLAY_LABEL)); + assertEquals(EVENT_TYPE, eventObj.get(KEY_EVENT_TYPE)); + + } + finally + { + eventService.removeEvent(eventName); + } + + // Test with no event name set + obj = new JSONObject(); + obj.put(KEY_EVENT_DISPLAY_LABEL, DISPLAY_LABEL); + obj.put(KEY_EVENT_TYPE, EVENT_TYPE); + rsp = sendRequest(new PostRequest(GET_EVENTS_URL, obj.toString(), APPLICATION_JSON),200); + try + { + String rspContent = rsp.getContentAsString(); + + JSONObject resultObj = new JSONObject(rspContent); + JSONObject eventObj = resultObj.getJSONObject("data"); + assertNotNull(eventObj); + + assertNotNull(eventObj.get(KEY_EVENT_NAME)); + assertEquals(DISPLAY_LABEL, eventObj.get(KEY_EVENT_DISPLAY_LABEL)); + assertEquals(EVENT_TYPE, eventObj.get(KEY_EVENT_TYPE)); + + eventName = eventObj.getString(KEY_EVENT_NAME); + } + finally + { + eventService.removeEvent(eventName); + } + } + + public void testPutRole() throws Exception + { + String eventName = GUID.generate(); + eventService.addEvent(EVENT_TYPE, eventName, DISPLAY_LABEL); + + try + { + JSONObject obj = new JSONObject(); + obj.put(KEY_EVENT_NAME, eventName); + obj.put(KEY_EVENT_DISPLAY_LABEL, "changed"); + obj.put(KEY_EVENT_TYPE, EVENT_TYPE); + + // Get the roles + Response rsp = sendRequest(new PutRequest(GET_EVENTS_URL + "/" + eventName, obj.toString(), APPLICATION_JSON),200); + String rspContent = rsp.getContentAsString(); + + JSONObject result = new JSONObject(rspContent); + JSONObject eventObj = result.getJSONObject("data"); + assertNotNull(eventObj); + + assertEquals(eventName, eventObj.get(KEY_EVENT_NAME)); + assertEquals("changed", eventObj.get(KEY_EVENT_DISPLAY_LABEL)); + assertEquals(EVENT_TYPE, eventObj.get(KEY_EVENT_TYPE)); + + // Bad requests + sendRequest(new PutRequest(GET_EVENTS_URL + "/cheese", obj.toString(), APPLICATION_JSON), 404); + } + finally + { + // Clean up + eventService.removeEvent(eventName); + } + + } + + public void testGetRole() throws Exception + { + String eventName = GUID.generate(); + eventService.addEvent(EVENT_TYPE, eventName, DISPLAY_LABEL); + + try + { + // Get the roles + Response rsp = sendRequest(new GetRequest(GET_EVENTS_URL + "/" + eventName),200); + String rspContent = rsp.getContentAsString(); + + JSONObject obj = new JSONObject(rspContent); + JSONObject eventObj = obj.getJSONObject("data"); + assertNotNull(eventObj); + + assertEquals(eventName, eventObj.get(KEY_EVENT_NAME)); + assertEquals(DISPLAY_LABEL, eventObj.get(KEY_EVENT_DISPLAY_LABEL)); + assertEquals(EVENT_TYPE, eventObj.get(KEY_EVENT_TYPE)); + + // Bad requests + sendRequest(new GetRequest(GET_EVENTS_URL + "/cheese"), 404); + } + finally + { + // Clean up + eventService.removeEvent(eventName); + } + + } + + public void testDeleteRole() throws Exception + { + String eventName = GUID.generate(); + assertFalse(eventService.existsEvent(eventName)); + eventService.addEvent(EVENT_TYPE, eventName, DISPLAY_LABEL); + assertTrue(eventService.existsEvent(eventName)); + sendRequest(new DeleteRequest(GET_EVENTS_URL + "/" + eventName),200); + assertFalse(eventService.existsEvent(eventName)); + + // Bad request + sendRequest(new DeleteRequest(GET_EVENTS_URL + "/cheese"), 404); + } + +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/webscript/RMCaveatConfigScriptTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/webscript/RMCaveatConfigScriptTest.java new file mode 100644 index 0000000000..c7e17a2ba6 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/webscript/RMCaveatConfigScriptTest.java @@ -0,0 +1,958 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.webscript; + +import java.util.ArrayList; +import java.util.List; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.caveat.RMCaveatConfigService; +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMWebScriptTestCase; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.util.PropertyMap; +import org.json.JSONArray; +import org.json.JSONObject; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.TestWebScriptServer.DeleteRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.PostRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.PutRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.Response; + +/** + * + * + * @author Mark Rogers + */ +public class RMCaveatConfigScriptTest extends BaseRMWebScriptTestCase +{ + private MutableAuthenticationService authenticationService; + private RMCaveatConfigService caveatConfigService; + private PersonService personService; + + + protected final static String RM_LIST = "rmc:smListTest"; + protected final static String RM_LIST_URI_ELEM = "rmc_smListTest"; + + private static final String URL_RM_CONSTRAINTS = "/api/rma/admin/rmconstraints"; + + @Override + protected void initServices() + { + super.initServices(); + + this.caveatConfigService = (RMCaveatConfigService)getServer().getApplicationContext().getBean("CaveatConfigService"); + this.authenticationService = (MutableAuthenticationService)getServer().getApplicationContext().getBean("AuthenticationService"); + this.personService = (PersonService)getServer().getApplicationContext().getBean("PersonService"); + } + + private void createUser(String userName) + { + if (this.authenticationService.authenticationExists(userName) == false) + { + this.authenticationService.createAuthentication(userName, "PWD".toCharArray()); + + PropertyMap ppOne = new PropertyMap(4); + ppOne.put(ContentModel.PROP_USERNAME, userName); + ppOne.put(ContentModel.PROP_AUTHORITY_DISPLAY_NAME, "title" + userName); + ppOne.put(ContentModel.PROP_FIRSTNAME, "firstName"); + ppOne.put(ContentModel.PROP_LASTNAME, "lastName"); + ppOne.put(ContentModel.PROP_EMAIL, "email@email.com"); + ppOne.put(ContentModel.PROP_JOBTITLE, "jobTitle"); + + this.personService.createPerson(ppOne); + } + } + + + public void testGetRMConstraints() throws Exception + { + { + Response response = sendRequest(new GetRequest(URL_RM_CONSTRAINTS), Status.STATUS_OK); + + JSONObject top = new JSONObject(response.getContentAsString()); + System.out.println(response.getContentAsString()); + JSONArray data = top.getJSONArray("data"); + } + + /** + * Add a list, then get it back via the list rest script + */ + caveatConfigService.addRMConstraint(RM_LIST, "my title", new String[0]); + + { + Response response = sendRequest(new GetRequest(URL_RM_CONSTRAINTS), Status.STATUS_OK); + JSONObject top = new JSONObject(response.getContentAsString()); + System.out.println(response.getContentAsString()); + JSONArray data = top.getJSONArray("data"); + + boolean found = false; + assertTrue("no data returned", data.length() > 0); + for(int i = 0; i < data.length(); i++) + { + JSONObject obj = data.getJSONObject(i); + String name = (String)obj.getString("constraintName"); + assertNotNull("constraintName is null", name); + String url = (String)obj.getString("url"); + assertNotNull("detail url is null", name); + if(name.equalsIgnoreCase(RM_LIST)) + { + found = true; + } + + /** + * vallidate the detail URL returned + */ + sendRequest(new GetRequest(url), Status.STATUS_OK); + } + } + } + + /** + * + * @throws Exception + */ + public void testGetRMConstraint() throws Exception + { + /** + * Delete the list to remove any junk then recreate it. + */ + if (caveatConfigService.getRMConstraint(RM_LIST) != null) + { + caveatConfigService.deleteRMConstraint(RM_LIST); + } + caveatConfigService.addRMConstraint(RM_LIST, "my title", new String[0]); + + + createUser("fbloggs"); + createUser("jrogers"); + createUser("jdoe"); + + + List values = new ArrayList(); + values.add("NOFORN"); + values.add("FGI"); + caveatConfigService.updateRMConstraintListAuthority(RM_LIST, "fbloggs", values); + caveatConfigService.updateRMConstraintListAuthority(RM_LIST, "jrogers", values); + caveatConfigService.updateRMConstraintListAuthority(RM_LIST, "jdoe", values); + + /** + * Positive test Get the constraint + */ + { + String url = URL_RM_CONSTRAINTS + "/" + RM_LIST_URI_ELEM; + Response response = sendRequest(new GetRequest(url), Status.STATUS_OK); + JSONObject top = new JSONObject(response.getContentAsString()); + + JSONObject data = top.getJSONObject("data"); + System.out.println(response.getContentAsString()); + + String constraintName = data.getString("constraintName"); + assertNotNull("constraintName is null", constraintName); + JSONArray allowedValues = data.getJSONArray("allowedValues"); + +// assertTrue("values not correct", compare(array, allowedValues)); + +// JSONArray constraintDetails = data.getJSONArray("constraintDetails"); +// +// assertTrue("details array does not contain 3 elements", constraintDetails.length() == 3); +// for(int i =0; i < constraintDetails.length(); i++) +// { +// JSONObject detail = constraintDetails.getJSONObject(i); +// } + } + + /** + * + * @throws Exception + */ + + /** + * Negative test - Attempt to get a constraint that does exist + */ + { + String url = URL_RM_CONSTRAINTS + "/" + "rmc_wibble"; + sendRequest(new GetRequest(url), Status.STATUS_NOT_FOUND); + } + + personService.deletePerson("fbloggs"); + personService.deletePerson("jrogers"); + personService.deletePerson("jdoe"); + + + + + } + + /** + * Create an RM Constraint + * @throws Exception + */ + public void testUpdateRMConstraint() throws Exception + { + + String constraintName = null; + /* + * Create a new list + */ + { + String title = "test Update RM Constraint title"; + JSONArray array = new JSONArray(); + array.put("LEMON"); + array.put("BANANA"); + array.put("PEACH"); + + JSONObject obj = new JSONObject(); + obj.put("allowedValues", array); + obj.put("constraintTitle", title); + /** + * Now do a post to create a new list + */ + Response response = sendRequest(new PostRequest(URL_RM_CONSTRAINTS, obj.toString(), "application/json"), Status.STATUS_OK); + JSONObject top = new JSONObject(response.getContentAsString()); + + JSONObject data = top.getJSONObject("data"); + constraintName = data.getString("constraintName"); + JSONArray allowedValues = data.getJSONArray("allowedValues"); + assertTrue("values not correct", compare(array, allowedValues)); + + } + + /** + * Now update both values and title - remove BANANA, PEACH, Add APPLE. + */ + + { + String newTitle = "this is the new title"; + JSONArray array = new JSONArray(); + array.put("LEMON"); + array.put("APPLE"); + + JSONObject obj = new JSONObject(); + obj.put("allowedValues", array); + obj.put("constraintName", constraintName); + obj.put("constraintTitle", newTitle); + + System.out.println(obj.toString()); + + /** + * Now do a post to update list + */ + Response response = sendRequest(new PutRequest(URL_RM_CONSTRAINTS + "/" + constraintName, obj.toString(), "application/json"), Status.STATUS_OK); + // Check the response + JSONObject top = new JSONObject(response.getContentAsString()); + JSONObject data = top.getJSONObject("data"); + + System.out.println(response.getContentAsString()); + + String url = data.getString("url"); + String constraintName2 = data.getString("constraintName"); + String constraintTitle = data.getString("constraintTitle"); + JSONArray allowedValues = data.getJSONArray("allowedValues"); + + assertTrue(allowedValues.length() == 2); + assertTrue("values not correct", compare(array, allowedValues)); + assertNotNull(url); + assertEquals(constraintName2, constraintName); + assertNotNull(constraintTitle); + assertEquals("title not as expected", constraintTitle, newTitle); + + // Check that data has been persisted. + Response resp2 = sendRequest(new GetRequest(url), Status.STATUS_OK); + JSONObject top2 = new JSONObject(resp2.getContentAsString()); + System.out.println("Problem here"); + System.out.println(resp2.getContentAsString()); + JSONObject data2 = top2.getJSONObject("data"); + String constraintTitle2 = data2.getString("constraintTitle"); + JSONArray allowedValues2 = data2.getJSONArray("allowedValues"); + assertTrue("values not correct", compare(array, allowedValues2)); + assertTrue("allowedValues is not 2", allowedValues2.length() == 2); + assertEquals(constraintName2, constraintName); + assertNotNull(constraintTitle2); + assertEquals("title not as expected", constraintTitle2, newTitle); + + } + + /** + * Now put without allowed values + */ + { + String newTitle = "update with no values"; + + JSONObject obj = new JSONObject(); + + obj.put("constraintName", RM_LIST); + obj.put("constraintTitle", newTitle); + + /** + * Now do a put to update a new list + */ + + Response response = sendRequest(new PutRequest(URL_RM_CONSTRAINTS + "/" + constraintName, obj.toString(), "application/json"), Status.STATUS_OK); + // Check the response + JSONObject top = new JSONObject(response.getContentAsString()); + + JSONObject data = top.getJSONObject("data"); + System.out.println(response.getContentAsString()); + + String url = data.getString("url"); + String constraintName2 = data.getString("constraintName"); + String constraintTitle = data.getString("constraintTitle"); + JSONArray allowedValues = data.getJSONArray("allowedValues"); + + assertTrue(allowedValues.length() == 2); + + assertNotNull(url); + assertEquals(constraintName2, constraintName); + assertNotNull(constraintTitle); + assertEquals("title not as expected", constraintTitle, newTitle); + } + + /** + * Now post without constraint Title + */ + { + JSONArray array = new JSONArray(); + array.put("LEMON"); + array.put("APPLE"); + + JSONObject obj = new JSONObject(); + obj.put("allowedValues", array); + + System.out.println(obj.toString()); + + /** + * Now do a Put to update the list - title should remain + */ + + Response response = sendRequest(new PutRequest(URL_RM_CONSTRAINTS + "/" + constraintName, obj.toString(), "application/json"), Status.STATUS_OK); + // Check the response + JSONObject top = new JSONObject(response.getContentAsString()); + + JSONObject data = top.getJSONObject("data"); + System.out.println(response.getContentAsString()); + } + + /** + * Add a new value (PEAR) to the list + */ + { + JSONArray array = new JSONArray(); + array.put("PEAR"); + array.put("LEMON"); + array.put("APPLE"); + + JSONObject obj = new JSONObject(); + obj.put("allowedValues", array); + + System.out.println(obj.toString()); + + Response response = sendRequest(new PutRequest(URL_RM_CONSTRAINTS + "/" + constraintName, obj.toString(), "application/json"), Status.STATUS_OK); + // Check the response + JSONObject top = new JSONObject(response.getContentAsString()); + + JSONObject data = top.getJSONObject("data"); + System.out.println(response.getContentAsString()); + } + + /** + * Remove a value (PEAR) from the list + */ + { + JSONArray array = new JSONArray(); + array.put("APPLE"); + array.put("LEMON"); + + JSONObject obj = new JSONObject(); + obj.put("allowedValues", array); + + System.out.println(obj.toString()); + + Response response = sendRequest(new PutRequest(URL_RM_CONSTRAINTS + "/" + constraintName, obj.toString(), "application/json"), Status.STATUS_OK); + // Check the response + JSONObject top = new JSONObject(response.getContentAsString()); + + JSONObject data = top.getJSONObject("data"); + System.out.println(response.getContentAsString()); + } + + } + + + /** + * Create an RM Constraint + * @throws Exception + */ + public void testCreateRMConstraint() throws Exception + { + /** + * Delete the list to remove any junk then recreate it. + */ + //caveatConfigService.deleteRMConstraint(RM_LIST); + + /** + * create a new list + */ + { + JSONArray array = new JSONArray(); + array.put("NOFORN"); + array.put("FGI"); + + JSONObject obj = new JSONObject(); + obj.put("allowedValues", array); + obj.put("constraintName", RM_LIST); + obj.put("constraintTitle", "this is the title"); + + System.out.println(obj.toString()); + + /** + * Now do a post to create a new list + */ + + Response response = sendRequest(new PostRequest(URL_RM_CONSTRAINTS, obj.toString(), "application/json"), Status.STATUS_OK); + // Check the response + } + + /** + * Now go and get the constraint + */ + { + String url = URL_RM_CONSTRAINTS + "/" + RM_LIST_URI_ELEM; + Response response = sendRequest(new GetRequest(url), Status.STATUS_OK); + JSONObject top = new JSONObject(response.getContentAsString()); + + JSONObject data = top.getJSONObject("data"); + System.out.println(response.getContentAsString()); + + String constraintName = data.getString("constraintName"); + assertNotNull("constraintName is null", constraintName); + +// JSONArray constraintDetails = data.getJSONArray("constraintDetails"); +// +// assertTrue("details array does not contain 3 elements", constraintDetails.length() == 3); +// for(int i =0; i < constraintDetails.length(); i++) +// { +// JSONObject detail = constraintDetails.getJSONObject(i); +// } + } + + /** + * Now a constraint with a generated name + */ + { + String title = "Generated title list"; + JSONArray array = new JSONArray(); + array.put("Red"); + array.put("Blue"); + array.put("Green"); + + JSONObject obj = new JSONObject(); + obj.put("allowedValues", array); + obj.put("constraintTitle", title); + + System.out.println(obj.toString()); + + /** + * Now do a post to create a new list + */ + Response response = sendRequest(new PostRequest(URL_RM_CONSTRAINTS, obj.toString(), "application/json"), Status.STATUS_OK); + JSONObject top = new JSONObject(response.getContentAsString()); + + JSONObject data = top.getJSONObject("data"); + System.out.println(response.getContentAsString()); + + // Check the response + + String url = data.getString("url"); + String constraintName = data.getString("constraintName"); + String constraintTitle = data.getString("constraintTitle"); + JSONArray allowedValues = data.getJSONArray("allowedValues"); + + assertTrue(allowedValues.length() == 3); + assertNotNull(url); + assertNotNull(constraintName); + assertNotNull(constraintTitle); + assertEquals("title not as expected", constraintTitle, title); + sendRequest(new GetRequest(url), Status.STATUS_OK); + + + } + + + /** + * Now a constraint with an empty list of values. + */ + { + JSONArray array = new JSONArray(); + + JSONObject obj = new JSONObject(); + obj.put("allowedValues", array); + obj.put("constraintName", "rmc_whazoo"); + obj.put("constraintTitle", "this is the title"); + + System.out.println(obj.toString()); + + /** + * Now do a post to create a new list + */ + + Response response = sendRequest(new PostRequest(URL_RM_CONSTRAINTS, obj.toString(), "application/json"), Status.STATUS_OK); + JSONObject top = new JSONObject(response.getContentAsString()); + + JSONObject data = top.getJSONObject("data"); + System.out.println(response.getContentAsString()); + + // Check the response + } + + +// /** +// * Negative tests - duplicate list +// */ +// { +// JSONArray array = new JSONArray(); +// array.put("NOFORN"); +// array.put("FGI"); +// +// JSONObject obj = new JSONObject(); +// obj.put("allowedValues", array); +// obj.put("constraintName", RM_LIST); +// obj.put("constraintTitle", "this is the title"); +// +// System.out.println(obj.toString()); +// +// /** +// * Now do a post to create a new list +// */ +// Response response = sendRequest(new PostRequest(URL_RM_CONSTRAINTS, obj.toString(), "application/json"), Status.STATUS_CREATED); +// JSONObject top = new JSONObject(response.getContentAsString()); +// +// JSONObject data = top.getJSONObject("data"); +// System.out.println(response.getContentAsString()); +// +// // Check the response +// } + + + } + + + public void testGetRMConstraintValues() throws Exception + { + createUser("fbloggs"); + createUser("jrogers"); + createUser("jdoe"); + + /** + * Delete the list to remove any junk then recreate it. + */ + { + if (caveatConfigService.getRMConstraint(RM_LIST) != null) + { + caveatConfigService.deleteRMConstraint(RM_LIST); + } + caveatConfigService.addRMConstraint(RM_LIST, "my title", new String[0]); + + List values = new ArrayList(); + values.add("NOFORN"); + values.add("FGI"); + caveatConfigService.updateRMConstraintListAuthority(RM_LIST, "fbloggs", values); + caveatConfigService.updateRMConstraintListAuthority(RM_LIST, "jrogers", values); + caveatConfigService.updateRMConstraintListAuthority(RM_LIST, "jdoe", values); + } + + /** + * Positive test Get the constraint + */ + { + String url = URL_RM_CONSTRAINTS + "/" + RM_LIST_URI_ELEM + "/values"; + Response response = sendRequest(new GetRequest(url), Status.STATUS_OK); + JSONObject top = new JSONObject(response.getContentAsString()); + + JSONObject data = top.getJSONObject("data"); + System.out.println(response.getContentAsString()); + + String constraintName = data.getString("constraintName"); + assertNotNull("constraintName is null", constraintName); + String constraintTitle = data.getString("constraintTitle"); + assertNotNull("constraintTitle is null", constraintTitle); + + JSONArray values = data.getJSONArray("values"); + + assertTrue("details array does not contain 2 elements", values.length() == 2); + boolean fgiFound = false; + boolean nofornFound = false; + + for(int i =0; i < values.length(); i++) + { + JSONObject value = values.getJSONObject(i); + + if(value.getString("valueName").equalsIgnoreCase("FGI")) + { + fgiFound = true; + + } + + if(value.getString("valueName").equalsIgnoreCase("NOFORN")) + { + nofornFound = true; + } + + + } + assertTrue("fgi not found", fgiFound); + assertTrue("noforn not found", nofornFound); + } + + personService.deletePerson("fbloggs"); + personService.deletePerson("jrogers"); + personService.deletePerson("jdoe"); + } + + + + /** + * Update a value in a constraint + * @throws Exception + */ + public void testUpdateRMConstraintValue() throws Exception + { + if (caveatConfigService.getRMConstraint(RM_LIST) != null) + { + caveatConfigService.deleteRMConstraint(RM_LIST); + } + caveatConfigService.addRMConstraint(RM_LIST, "my title", new String[0]); + + /** + * Add some data to an empty list + */ + { + JSONArray values = new JSONArray(); + + JSONArray authorities = new JSONArray(); + authorities.put("fbloggs"); + authorities.put("jdoe"); + + JSONObject valueA = new JSONObject(); + valueA.put("value", "NOFORN"); + valueA.put("authorities", authorities); + + values.put(valueA); + + JSONObject valueB = new JSONObject(); + valueB.put("value", "FGI"); + valueB.put("authorities", authorities); + + values.put(valueB); + + JSONObject obj = new JSONObject(); + obj.put("values", values); + + + /** + * Do the first update - should get back + * NOFORN - fbloggs, jdoe + * FGI - fbloggs, jdoe + */ + Response response = sendRequest(new PostRequest(URL_RM_CONSTRAINTS + "/" + RM_LIST + "/values" , obj.toString(), "application/json"), Status.STATUS_OK); + JSONObject top = new JSONObject(response.getContentAsString()); + + JSONObject data = top.getJSONObject("data"); + System.out.println(response.getContentAsString()); + assertNotNull("data is null", data); + + JSONArray myValues = data.getJSONArray("values"); + assertTrue("two values not found", myValues.length() == 2); + for(int i = 0; i < myValues.length(); i++) + { + JSONObject myObj = myValues.getJSONObject(i); + } + } + + /** + * Add to a new value, NOCON, fbloggs, jrogers + */ + { + JSONArray values = new JSONArray(); + + JSONArray authorities = new JSONArray(); + authorities.put("fbloggs"); + authorities.put("jrogers"); + + JSONObject valueA = new JSONObject(); + valueA.put("value", "NOCON"); + valueA.put("authorities", authorities); + + values.put(valueA); + + + JSONObject obj = new JSONObject(); + obj.put("values", values); + + + /** + * Add a new value - should get back + * NOFORN - fbloggs, jdoe + * FGI - fbloggs, jdoe + * NOCON - fbloggs, jrogers + */ + System.out.println(obj.toString()); + Response response = sendRequest(new PostRequest(URL_RM_CONSTRAINTS + "/" + RM_LIST + "/values" , obj.toString(), "application/json"), Status.STATUS_OK); + JSONObject top = new JSONObject(response.getContentAsString()); + + JSONObject data = top.getJSONObject("data"); + System.out.println(response.getContentAsString()); + assertNotNull("data is null", data); + + JSONArray myValues = data.getJSONArray("values"); + assertTrue("three values not found", myValues.length() == 3); + for(int i = 0; i < myValues.length(); i++) + { + JSONObject myObj = myValues.getJSONObject(i); + } + } + + /** + * Add to an existing value (NOFORN, jrogers) + * should get back + * NOFORN - fbloggs, jdoe, jrogers + * FGI - fbloggs, jdoe + * NOCON - fbloggs, jrogers + */ + { + JSONArray values = new JSONArray(); + + JSONArray authorities = new JSONArray(); + authorities.put("fbloggs"); + authorities.put("jrogers"); + authorities.put("jdoe"); + + JSONObject valueA = new JSONObject(); + valueA.put("value", "NOFORN"); + valueA.put("authorities", authorities); + + values.put(valueA); + + + JSONObject obj = new JSONObject(); + obj.put("values", values); + + Response response = sendRequest(new PostRequest(URL_RM_CONSTRAINTS + "/" + RM_LIST + "/values" , obj.toString(), "application/json"), Status.STATUS_OK); + JSONObject top = new JSONObject(response.getContentAsString()); + + JSONObject data = top.getJSONObject("data"); + System.out.println(response.getContentAsString()); + assertNotNull("data is null", data); + + JSONArray myValues = data.getJSONArray("values"); + assertTrue("three values not found", myValues.length() == 3); + for(int i = 0; i < myValues.length(); i++) + { + JSONObject myObj = myValues.getJSONObject(i); + } + } + + + /** + * Remove from existing value (NOCON, fbloggs) + */ + { + JSONArray values = new JSONArray(); + + JSONArray authorities = new JSONArray(); + authorities.put("jrogers"); + + JSONObject valueA = new JSONObject(); + valueA.put("value", "NOCON"); + valueA.put("authorities", authorities); + + values.put(valueA); + + + JSONObject obj = new JSONObject(); + obj.put("values", values); + + + /** + * should get back + * NOFORN - fbloggs, jdoe + * FGI - fbloggs, jdoe + * NOCON - jrogers + */ + Response response = sendRequest(new PostRequest(URL_RM_CONSTRAINTS + "/" + RM_LIST + "/values" , obj.toString(), "application/json"), Status.STATUS_OK); + JSONObject top = new JSONObject(response.getContentAsString()); + + JSONObject data = top.getJSONObject("data"); + System.out.println(response.getContentAsString()); + assertNotNull("data is null", data); + + JSONArray myValues = data.getJSONArray("values"); + assertTrue("three values not found", myValues.length() == 3); + boolean foundNOCON = false; + boolean foundNOFORN = false; + boolean foundFGI = false; + + for(int i = 0; i < myValues.length(); i++) + { + JSONObject myObj = myValues.getJSONObject(i); + + if(myObj.getString("valueName").equalsIgnoreCase("NOCON")) + { + foundNOCON = true; + } + if(myObj.getString("valueName").equalsIgnoreCase("NOFORN")) + { + foundNOFORN = true; + } + if(myObj.getString("valueName").equalsIgnoreCase("FGI")) + { + foundFGI = true; + } + } + + assertTrue("not found NOCON", foundNOCON); + assertTrue("not found NOFORN", foundNOFORN); + assertTrue("not found FGI", foundFGI); + } + } + + + /** + * Delete the entire constraint + * + * @throws Exception + */ + public void testDeleteRMConstraint() throws Exception + { + /** + * Delete the list to remove any junk then recreate it. + */ + if (caveatConfigService.getRMConstraint(RM_LIST) != null) + { + caveatConfigService.deleteRMConstraint(RM_LIST); + } + caveatConfigService.addRMConstraint(RM_LIST, "my title", new String[0]); + + /** + * Now do a delete + */ + Response response = sendRequest(new DeleteRequest(URL_RM_CONSTRAINTS + "/" + RM_LIST), Status.STATUS_OK); + + /** + * Now delete the list that should have been deleted + */ + // TODO NEED TO THINK ABOUT THIS BEHAVIOUR + //{ + // sendRequest(new DeleteRequest(URL_RM_CONSTRAINTS + "/" + RM_LIST), Status.STATUS_NOT_FOUND); + //} + + /** + * Negative test - delete list that does not exist + */ + { + sendRequest(new DeleteRequest(URL_RM_CONSTRAINTS + "/" + "rmc_wibble"), Status.STATUS_NOT_FOUND); + } + } + + private boolean compare(JSONArray from, JSONArray to) throws Exception + { + List ret = new ArrayList(); + + if(from.length() != to.length()) + { + fail("arrays are different lengths" + from.length() +", " + to.length()); + return false; + } + + for(int i = 0 ; i < to.length(); i++) + { + ret.add(to.getString(i)); + } + + for(int i = 0 ; i < from.length(); i++) + { + String val = from.getString(i); + + if(ret.contains(val)) + { + + } + else + { + fail("Value not contained in list:" + val); + return false; + } + } + + return true; + } + + + /** + * Create an RM Constraint value + * @throws Exception + */ + public void testGetRMConstraintValue() throws Exception + { + + String constraintName = null; + + /* + * Create a new list + */ + { + String title = "Get Constraint Value"; + JSONArray array = new JSONArray(); + array.put("POTATO"); + array.put("CARROT"); + array.put("TURNIP"); + + JSONObject obj = new JSONObject(); + obj.put("allowedValues", array); + obj.put("constraintTitle", title); + /** + * Now do a post to create a new list + */ + Response response = sendRequest(new PostRequest(URL_RM_CONSTRAINTS, obj.toString(), "application/json"), Status.STATUS_OK); + JSONObject top = new JSONObject(response.getContentAsString()); + + JSONObject data = top.getJSONObject("data"); + constraintName = data.getString("constraintName"); + JSONArray allowedValues = data.getJSONArray("allowedValues"); + assertTrue("values not correct", compare(array, allowedValues)); + } + + /** + * Get the CARROT value + */ + { + String url = URL_RM_CONSTRAINTS + "/" + constraintName + "/values/" + "CARROT"; + Response response = sendRequest(new GetRequest(url), Status.STATUS_OK); + JSONObject top = new JSONObject(response.getContentAsString()); + } + + { + String url = URL_RM_CONSTRAINTS + "/" + constraintName + "/values/" + "ONION"; + sendRequest(new GetRequest(url), Status.STATUS_NOT_FOUND); + } + } +} + diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/webscript/RMConstraintScriptTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/webscript/RMConstraintScriptTest.java new file mode 100644 index 0000000000..6116258954 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/webscript/RMConstraintScriptTest.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.webscript; + +import java.util.ArrayList; +import java.util.List; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.caveat.RMCaveatConfigService; +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMWebScriptTestCase; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.util.PropertyMap; +import org.json.JSONObject; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.Response; + +/** + * Test of GET RM Constraint (User facing scripts) + * + * @author Mark Rogers + */ +public class RMConstraintScriptTest extends BaseRMWebScriptTestCase +{ + private MutableAuthenticationService authenticationService; + private RMCaveatConfigService caveatConfigService; + private PersonService personService; + + protected final static String RM_LIST = "rmc:smListTest"; + protected final static String RM_LIST_URI_ELEM = "rmc_smListTest"; + + private static final String URL_RM_CONSTRAINTS = "/api/rma/rmconstraints"; + + @Override + protected void initServices() + { + super.initServices(); + + this.caveatConfigService = (RMCaveatConfigService)getServer().getApplicationContext().getBean("CaveatConfigService"); + this.authenticationService = (MutableAuthenticationService)getServer().getApplicationContext().getBean("AuthenticationService"); + this.personService = (PersonService)getServer().getApplicationContext().getBean("PersonService"); + } + + /** + * + * @throws Exception + */ + public void testGetRMConstraint() throws Exception + { + // Set the current security context as admin + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + + /** + * Delete the list to remove any junk then recreate it. + */ + if (caveatConfigService.getRMConstraint(RM_LIST) != null) + { + caveatConfigService.deleteRMConstraint(RM_LIST); + } + caveatConfigService.addRMConstraint(RM_LIST, "my title", new String[0]); + + + createUser("fbloggs"); + createUser("jrogers"); + createUser("jdoe"); + + + List values = new ArrayList(); + values.add("NOFORN"); + values.add("FGI"); + caveatConfigService.updateRMConstraintListAuthority(RM_LIST, "fbloggs", values); + caveatConfigService.updateRMConstraintListAuthority(RM_LIST, "jrogers", values); + caveatConfigService.updateRMConstraintListAuthority(RM_LIST, "jdoe", values); + + AuthenticationUtil.setFullyAuthenticatedUser("jdoe"); + /** + * Positive test Get the constraint + */ + { + String url = URL_RM_CONSTRAINTS + "/" + RM_LIST_URI_ELEM; + Response response = sendRequest(new GetRequest(url), Status.STATUS_OK); + JSONObject top = new JSONObject(response.getContentAsString()); + + JSONObject data = top.getJSONObject("data"); + System.out.println(response.getContentAsString()); + + data.getJSONArray("allowedValuesForCurrentUser"); + + } + + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + personService.deletePerson("fbloggs"); + personService.deletePerson("jrogers"); + personService.deletePerson("jdoe"); + + } + + private void createUser(String userName) + { + if (this.authenticationService.authenticationExists(userName) == false) + { + this.authenticationService.createAuthentication(userName, "PWD".toCharArray()); + + PropertyMap ppOne = new PropertyMap(4); + ppOne.put(ContentModel.PROP_USERNAME, userName); + ppOne.put(ContentModel.PROP_AUTHORITY_DISPLAY_NAME, "title" + userName); + ppOne.put(ContentModel.PROP_FIRSTNAME, "firstName"); + ppOne.put(ContentModel.PROP_LASTNAME, "lastName"); + ppOne.put(ContentModel.PROP_EMAIL, "email@email.com"); + ppOne.put(ContentModel.PROP_JOBTITLE, "jobTitle"); + + this.personService.createPerson(ppOne); + } + } +} + + + \ No newline at end of file diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/webscript/RmRestApiTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/webscript/RmRestApiTest.java new file mode 100644 index 0000000000..24e0cce361 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/webscript/RmRestApiTest.java @@ -0,0 +1,1219 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.webscript; + +import java.io.IOException; +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.text.MessageFormat; +import java.util.Date; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminServiceImpl; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementCustomModel; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.script.CustomReferenceType; +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMWebScriptTestCase; +import org.alfresco.module.org_alfresco_module_rm.test.util.TestActionParams; +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONStringer; +import org.json.JSONTokener; +import org.springframework.extensions.surf.util.ISO8601DateFormat; +import org.springframework.extensions.webscripts.TestWebScriptServer.DeleteRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.PostRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.PutRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.Response; + +/** + * This class tests the Rest API for RM. + * + * @author Neil McErlean + */ +public class RmRestApiTest extends BaseRMWebScriptTestCase implements RecordsManagementModel +{ + protected static final String GET_NODE_AUDITLOG_URL_FORMAT = "/api/node/{0}/rmauditlog"; + protected static final String GET_TRANSFER_URL_FORMAT = "/api/node/{0}/transfers/{1}"; + protected static final String TRANSFER_REPORT_URL_FORMAT = "/api/node/{0}/transfers/{1}/report"; + protected static final String REF_INSTANCES_URL_FORMAT = "/api/node/{0}/customreferences"; + protected static final String RMA_AUDITLOG_URL = "/api/rma/admin/rmauditlog"; + protected static final String RMA_AUDITLOG_STATUS_URL = "/api/rma/admin/rmauditlog/status"; + protected static final String GET_LIST_URL = "/api/rma/admin/listofvalues"; + protected static final String RMA_ACTIONS_URL = "/api/rma/actions/ExecutionQueue"; + protected static final String APPLICATION_JSON = "application/json"; + protected static final String RMA_CUSTOM_PROPS_DEFINITIONS_URL = "/api/rma/admin/custompropertydefinitions"; + protected static final String RMA_CUSTOM_REFS_DEFINITIONS_URL = "/api/rma/admin/customreferencedefinitions"; +// protected NamespaceService namespaceService; +// protected NodeService nodeService; +// protected ContentService contentService; +// protected DictionaryService dictionaryService; +// protected SearchService searchService; +// protected ImporterService importService; +// protected TransactionService transactionService; +// protected ServiceRegistry services; +// protected RecordsManagementService rmService; +// protected RecordsManagementActionService rmActionService; +// protected RecordsManagementAuditService rmAuditService; +// protected RecordsManagementAdminService rmAdminService; +// protected RetryingTransactionHelper transactionHelper; +// protected DispositionService dispositionService; + + private static final String BI_DI = "BiDi"; + private static final String CHILD_SRC = "childSrc"; + private static final String CHILD_TGT = "childTgt"; + +// @Override +// protected void setUp() throws Exception +// { +// setCustomContext("classpath:test-context.xml"); +// +// super.setUp(); +// this.namespaceService = (NamespaceService) getServer().getApplicationContext().getBean("NamespaceService"); +// this.nodeService = (NodeService) getServer().getApplicationContext().getBean("NodeService"); +// this.contentService = (ContentService)getServer().getApplicationContext().getBean("ContentService"); +// this.dictionaryService = (DictionaryService)getServer().getApplicationContext().getBean("DictionaryService"); +// this.searchService = (SearchService)getServer().getApplicationContext().getBean("SearchService"); +// this.importService = (ImporterService)getServer().getApplicationContext().getBean("ImporterService"); +// this.transactionService = (TransactionService)getServer().getApplicationContext().getBean("TransactionService"); +// this.services = (ServiceRegistry)getServer().getApplicationContext().getBean("ServiceRegistry"); +// this.rmService = (RecordsManagementService)getServer().getApplicationContext().getBean("RecordsManagementService"); +// this.rmActionService = (RecordsManagementActionService)getServer().getApplicationContext().getBean("RecordsManagementActionService"); +// this.rmAuditService = (RecordsManagementAuditService)getServer().getApplicationContext().getBean("RecordsManagementAuditService"); +// this.rmAdminService = (RecordsManagementAdminService)getServer().getApplicationContext().getBean("RecordsManagementAdminService"); +// transactionHelper = (RetryingTransactionHelper)getServer().getApplicationContext().getBean("retryingTransactionHelper"); +// dispositionService = (DispositionService)getServer().getApplicationContext().getBean("DispositionService"); +// +// AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); +// +// // Bring the filePlan into the test database. +// // +// // This is quite a slow call, so if this class grew to have many test methods, +// // there would be a real benefit in using something like @BeforeClass for the line below. +// transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() +// { +// public NodeRef execute() throws Throwable +// { +// return TestUtilities.loadFilePlanData(getServer().getApplicationContext()); +// } +// }); +// } + + /** + * This test method ensures that a POST of an RM action to a non-existent node + * will result in a 404 status. + * + * @throws Exception + */ + public void testPostActionToNonExistentNode() throws Exception + { + NodeRef nonExistentNode = new NodeRef("workspace://SpacesStore/09ca1e02-1c87-4a53-97e7-xxxxxxxxxxxx"); + + // Construct the JSON request. + JSONObject jsonPostData = new JSONObject(); + jsonPostData.put("nodeRef", nonExistentNode.toString()); + // Although the request specifies a 'reviewed' action, it does not matter what + // action is specified here, as the non-existent Node should trigger a 404 + // before the action is executed. + jsonPostData.put("name", "reviewed"); + + // Submit the JSON request. + String jsonPostString = jsonPostData.toString(); + + final int expectedStatus = 404; + sendRequest(new PostRequest(RMA_ACTIONS_URL, jsonPostString, APPLICATION_JSON), expectedStatus); + } + + public void testPostReviewedAction() throws IOException, JSONException + { + NodeRef testRecord = utils.createRecord(recordFolder, "test.txt"); + + // In this test, this property has a date-value equal to the model import time. + Serializable pristineReviewAsOf = this.nodeService.getProperty(testRecord, PROP_REVIEW_AS_OF); + + // Construct the JSON request for 'reviewed'. + String jsonString = new JSONStringer().object() + .key("name").value("reviewed") + .key("nodeRef").value(testRecord.toString()) + // These JSON params are just to test the submission of params. They'll be ignored. + .key("params").object() + .key("param1").value("one") + .key("param2").value("two") + .endObject() + .endObject() + .toString(); + + // Submit the JSON request. + final int expectedStatus = 200; + Response rsp = sendRequest(new PostRequest(RMA_ACTIONS_URL, + jsonString, APPLICATION_JSON), expectedStatus); + + String rspContent = rsp.getContentAsString(); + assertTrue(rspContent.contains("Successfully queued action [reviewed]")); + + Serializable newReviewAsOfDate = this.nodeService.getProperty(testRecord, PROP_REVIEW_AS_OF); + assertFalse("The reviewAsOf property should have changed. Was " + pristineReviewAsOf, + pristineReviewAsOf.equals(newReviewAsOfDate)); + } + + public void testPostMultiReviewedAction() throws IOException, JSONException + { + NodeRef testRecord = utils.createRecord(recordFolder, "test1.txt"); + NodeRef testRecord2 = utils.createRecord(recordFolder, "test2.txt"); + NodeRef testRecord3 = utils.createRecord(recordFolder, "test3.txt"); + + // In this test, this property has a date-value equal to the model import time. + Serializable pristineReviewAsOf = this.nodeService.getProperty(testRecord, PROP_REVIEW_AS_OF); + Serializable pristineReviewAsOf2 = this.nodeService.getProperty(testRecord2, PROP_REVIEW_AS_OF); + Serializable pristineReviewAsOf3 = this.nodeService.getProperty(testRecord3, PROP_REVIEW_AS_OF); + + // Construct the JSON request for 'reviewed'. + String jsonString = new JSONStringer().object() + .key("name").value("reviewed") + .key("nodeRefs").array() + .value(testRecord.toString()) + .value(testRecord2.toString()) + .value(testRecord3.toString()) + .endArray() + // These JSON params are just to test the submission of params. They'll be ignored. + .key("params").object() + .key("param1").value("one") + .key("param2").value("two") + .endObject() + .endObject() + .toString(); + + // Submit the JSON request. + final int expectedStatus = 200; + Response rsp = sendRequest(new PostRequest(RMA_ACTIONS_URL, + jsonString, APPLICATION_JSON), expectedStatus); + + String rspContent = rsp.getContentAsString(); + assertTrue(rspContent.contains("Successfully queued action [reviewed]")); + + Serializable newReviewAsOfDate = this.nodeService.getProperty(testRecord, PROP_REVIEW_AS_OF); + assertFalse("The reviewAsOf property should have changed. Was " + pristineReviewAsOf, + pristineReviewAsOf.equals(newReviewAsOfDate)); + Serializable newReviewAsOfDate2 = this.nodeService.getProperty(testRecord2, PROP_REVIEW_AS_OF); + assertFalse("The reviewAsOf property should have changed. Was " + pristineReviewAsOf2, + pristineReviewAsOf2.equals(newReviewAsOfDate2)); + Serializable newReviewAsOfDate3 = this.nodeService.getProperty(testRecord3, PROP_REVIEW_AS_OF); + assertFalse("The reviewAsOf property should have changed. Was " + pristineReviewAsOf3, + pristineReviewAsOf3.equals(newReviewAsOfDate3)); + } + + public void testActionParams() throws Exception + { + // Construct the JSON request for 'reviewed'. + String jsonString = new JSONStringer().object() + .key("name").value("testActionParams") + .key("nodeRef").array() + .value("nothing://nothing/nothing") + .endArray() + // These JSON params are just to test the submission of params. They'll be ignored. + .key("params").object() + .key(TestActionParams.PARAM_DATE).object() + .key("iso8601") + .value(ISO8601DateFormat.format(new Date())) + .endObject() + .endObject() + .endObject() + .toString(); + + // Submit the JSON request. + final int expectedStatus = 200; + //TODO Currently failing unit test. + sendRequest(new PostRequest(RMA_ACTIONS_URL, + jsonString, APPLICATION_JSON), expectedStatus); + } + + public void testPostCustomReferenceDefinitions() throws IOException, JSONException + { + postCustomReferenceDefinitions(); + } + + /** + * This method creates a child and a non-child reference and returns their generated ids. + * + * + * @return String[] with element 0 = refId of p/c ref, 1 = refId pf bidi. + */ + private String[] postCustomReferenceDefinitions() throws JSONException, IOException, + UnsupportedEncodingException { + String[] result = new String[2]; + + // 1. Child association. + String jsonString = new JSONStringer().object() + .key("referenceType").value(CustomReferenceType.PARENT_CHILD) + .key("source").value(CHILD_SRC) + .key("target").value(CHILD_TGT) + .endObject() + .toString(); + +// System.out.println(jsonString); + + // Submit the JSON request. + final int expectedStatus = 200; + Response rsp = sendRequest(new PostRequest(RMA_CUSTOM_REFS_DEFINITIONS_URL, + jsonString, APPLICATION_JSON), expectedStatus); + + String rspContent = rsp.getContentAsString(); + assertTrue(rspContent.contains("success")); + +// System.out.println(rspContent); + + JSONObject jsonRsp = new JSONObject(new JSONTokener(rspContent)); + String generatedChildRefId = jsonRsp.getJSONObject("data").getString("refId"); + result[0] = generatedChildRefId; + + // 2. Non-child or standard association. + jsonString = new JSONStringer().object() + .key("referenceType").value(CustomReferenceType.BIDIRECTIONAL) + .key("label").value(BI_DI) + .endObject() + .toString(); + +// System.out.println(jsonString); + + // Submit the JSON request. + rsp = sendRequest(new PostRequest(RMA_CUSTOM_REFS_DEFINITIONS_URL, + jsonString, APPLICATION_JSON), expectedStatus); + + rspContent = rsp.getContentAsString(); + assertTrue(rspContent.contains("success")); + +// System.out.println(rspContent); + + jsonRsp = new JSONObject(new JSONTokener(rspContent)); + String generatedBidiRefId = jsonRsp.getJSONObject("data").getString("refId"); + result[1] = generatedBidiRefId; + + // Now assert that both have appeared in the data dictionary. + AspectDefinition customAssocsAspect = + dictionaryService.getAspect(QName.createQName(RecordsManagementAdminServiceImpl.RMC_CUSTOM_ASSOCS, namespaceService)); + assertNotNull("Missing customAssocs aspect", customAssocsAspect); + + QName newRefQname = adminService.getQNameForClientId(generatedChildRefId); + Map associations = customAssocsAspect.getAssociations(); + assertTrue("Custom child assoc not returned by dataDictionary.", associations.containsKey(newRefQname)); + + newRefQname = adminService.getQNameForClientId(generatedBidiRefId); + assertTrue("Custom std assoc not returned by dataDictionary.", customAssocsAspect.getAssociations().containsKey(newRefQname)); + + return result; + } + + public void testPutCustomPropertyDefinition() throws Exception + { + // POST to create a property definition with a known propId + final String propertyLabel = "Original label åçîéøü"; + String propId = postCustomPropertyDefinition(propertyLabel, null); + + // PUT an updated label. + final String updatedLabel = "Updated label πø^¨¥†®"; + String jsonString = new JSONStringer().object() + .key("label").value(updatedLabel) + .endObject() + .toString(); + + String propDefnUrl = "/api/rma/admin/custompropertydefinitions/" + propId; + Response rsp = sendRequest(new PutRequest(propDefnUrl, + jsonString, APPLICATION_JSON), 200); + + // GET from the URL again to ensure it's valid + rsp = sendRequest(new GetRequest(propDefnUrl), 200); + String rspContent = rsp.getContentAsString(); + +// System.out.println(rspContent); + + // PUT an updated constraint ref. + final String updatedConstraint = "rmc:tlList"; + jsonString = new JSONStringer().object() + .key("constraintRef").value(updatedConstraint) + .endObject() + .toString(); + + propDefnUrl = "/api/rma/admin/custompropertydefinitions/" + propId; + rsp = sendRequest(new PutRequest(propDefnUrl, + jsonString, APPLICATION_JSON), 200); + + rspContent = rsp.getContentAsString(); + + JSONObject jsonRsp = new JSONObject(new JSONTokener(rspContent)); + String urlOfNewPropDef = jsonRsp.getString("url"); + assertNotNull("urlOfNewPropDef was null.", urlOfNewPropDef); + + // GET from the URL again to ensure it's valid + rsp = sendRequest(new GetRequest(propDefnUrl), 200); + rspContent = rsp.getContentAsString(); + +// System.out.println(rspContent); + + jsonRsp = new JSONObject(new JSONTokener(rspContent)); + JSONObject dataObject = jsonRsp.getJSONObject("data"); + assertNotNull("JSON data object was null", dataObject); + JSONObject customPropsObject = dataObject.getJSONObject("customProperties"); + assertNotNull("JSON customProperties object was null", customPropsObject); + assertEquals("Wrong customProperties length.", 1, customPropsObject.length()); + + Object keyToSoleProp = customPropsObject.keys().next(); + + JSONObject newPropObject = customPropsObject.getJSONObject((String)keyToSoleProp); + assertEquals("Wrong property label.", updatedLabel, newPropObject.getString("label")); + JSONArray constraintRefsArray = newPropObject.getJSONArray("constraintRefs"); + assertEquals("ConstraintRefsArray wrong length.", 1, constraintRefsArray.length()); + String retrievedUpdatedTitle = constraintRefsArray.getJSONObject(0).getString("name"); + assertEquals("Constraints had wrong name.", "rmc:tlList", retrievedUpdatedTitle); + + // PUT again to remove all constraints + jsonString = new JSONStringer().object() + .key("constraintRef").value(null) + .endObject() + .toString(); + + rsp = sendRequest(new PutRequest(propDefnUrl, + jsonString, APPLICATION_JSON), 200); + + rspContent = rsp.getContentAsString(); +// System.out.println(rspContent); + + jsonRsp = new JSONObject(new JSONTokener(rspContent)); + + // GET from the URL again to ensure it's valid + rsp = sendRequest(new GetRequest(propDefnUrl), 200); + rspContent = rsp.getContentAsString(); + +// System.out.println(rspContent); + + jsonRsp = new JSONObject(new JSONTokener(rspContent)); + dataObject = jsonRsp.getJSONObject("data"); + assertNotNull("JSON data object was null", dataObject); + customPropsObject = dataObject.getJSONObject("customProperties"); + assertNotNull("JSON customProperties object was null", customPropsObject); + assertEquals("Wrong customProperties length.", 1, customPropsObject.length()); + + keyToSoleProp = customPropsObject.keys().next(); + + newPropObject = customPropsObject.getJSONObject((String)keyToSoleProp); + assertEquals("Wrong property label.", updatedLabel, newPropObject.getString("label")); + constraintRefsArray = newPropObject.getJSONArray("constraintRefs"); + assertEquals("ConstraintRefsArray wrong length.", 0, constraintRefsArray.length()); + + // Finally PUT a constraint on a PropertyDefn that has been cleared of constraints. + // This was raised as an issue + final String readdedConstraint = "rmc:tlList"; + jsonString = new JSONStringer().object() + .key("constraintRef").value(readdedConstraint) + .endObject() + .toString(); + + propDefnUrl = "/api/rma/admin/custompropertydefinitions/" + propId; + rsp = sendRequest(new PutRequest(propDefnUrl, + jsonString, APPLICATION_JSON), 200); + + rspContent = rsp.getContentAsString(); + + jsonRsp = new JSONObject(new JSONTokener(rspContent)); +// System.out.println("PUTting a constraint back again."); +// System.out.println(rspContent); + + // And GET from the URL again + rsp = sendRequest(new GetRequest(propDefnUrl), 200); + rspContent = rsp.getContentAsString(); + +// System.out.println(rspContent); + + jsonRsp = new JSONObject(new JSONTokener(rspContent)); + dataObject = jsonRsp.getJSONObject("data"); + assertNotNull("JSON data object was null", dataObject); + customPropsObject = dataObject.getJSONObject("customProperties"); + assertNotNull("JSON customProperties object was null", customPropsObject); + assertEquals("Wrong customProperties length.", 1, customPropsObject.length()); + + keyToSoleProp = customPropsObject.keys().next(); + + newPropObject = customPropsObject.getJSONObject((String)keyToSoleProp); + assertEquals("Wrong property label.", updatedLabel, newPropObject.getString("label")); + constraintRefsArray = newPropObject.getJSONArray("constraintRefs"); + assertEquals("ConstraintRefsArray wrong length.", 1, constraintRefsArray.length()); + String readdedUpdatedTitle = constraintRefsArray.getJSONObject(0).getString("name"); + assertEquals("Constraints had wrong name.", "rmc:tlList", readdedUpdatedTitle); + } + + public void testGetCustomReferences() throws IOException, JSONException + { + // Ensure that there is at least one custom reference. + postCustomReferenceDefinitions(); + + // GET all custom reference definitions + final int expectedStatus = 200; + Response rsp = sendRequest(new GetRequest(RMA_CUSTOM_REFS_DEFINITIONS_URL), expectedStatus); + + JSONObject jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); + + JSONObject dataObj = (JSONObject)jsonRsp.get("data"); + assertNotNull("JSON 'data' object was null", dataObj); + + JSONArray customRefsObj = (JSONArray)dataObj.get("customReferences"); + assertNotNull("JSON 'customReferences' object was null", customRefsObj); + +// for (int i = 0; i < customRefsObj.length(); i++) { +// System.out.println(customRefsObj.getString(i)); +// } + + assertTrue("There should be at least two custom references. Found " + customRefsObj, customRefsObj.length() >= 2); + + // GET a specific custom reference definition. + // Here, we're using one of the built-in references + // qname = rmc:versions + rsp = sendRequest(new GetRequest(RMA_CUSTOM_REFS_DEFINITIONS_URL + "/" + "versions"), expectedStatus); + + jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); + + dataObj = (JSONObject)jsonRsp.get("data"); + assertNotNull("JSON 'data' object was null", dataObj); + + customRefsObj = (JSONArray)dataObj.get("customReferences"); + assertNotNull("JSON 'customProperties' object was null", customRefsObj); + + assertTrue("There should be exactly 1 custom references. Found " + customRefsObj.length(), customRefsObj.length() == 1); + } + + public void testGetDodCustomTypes() throws IOException, JSONException + { + final int expectedStatus = 200; + Response rsp = sendRequest(new GetRequest("/api/rma/admin/dodcustomtypes"), expectedStatus); + + String rspContent = rsp.getContentAsString(); + JSONObject jsonRsp = new JSONObject(new JSONTokener(rspContent)); + + // System.out.println(rspContent); + + JSONObject dataObj = (JSONObject)jsonRsp.get("data"); + assertNotNull("JSON 'data' object was null", dataObj); + + JSONArray customTypesObj = (JSONArray)dataObj.get("dodCustomTypes"); + assertNotNull("JSON 'dodCustomTypes' object was null", customTypesObj); + + assertEquals("Wrong DOD custom types count.", 4, customTypesObj.length()); + } + + public void testGetPostAndRemoveCustomReferenceInstances() throws Exception + { + NodeRef testRecord1 = utils.createRecord(recordFolder, "testRecord1" + System.currentTimeMillis(), "The from recørd"); + NodeRef testRecord2 = utils.createRecord(recordFolder, "testRecord2" + System.currentTimeMillis(), "The to récord"); + + String node1Url = testRecord1.toString().replace("://", "/"); + String refInstancesRecord1Url = MessageFormat.format(REF_INSTANCES_URL_FORMAT, node1Url); + + // Create reference types. + String[] generatedRefIds = postCustomReferenceDefinitions(); + + // Add a standard ref + String jsonString = new JSONStringer().object() + .key("toNode").value(testRecord2.toString()) + .key("refId").value(generatedRefIds[1]) + .endObject() + .toString(); + + Response rsp = sendRequest(new PostRequest(refInstancesRecord1Url, + jsonString, APPLICATION_JSON), 200); + + // Add a child ref + jsonString = new JSONStringer().object() + .key("toNode").value(testRecord2.toString()) + .key("refId").value(generatedRefIds[0]) + .endObject() + .toString(); + +// System.out.println(jsonString); + + rsp = sendRequest(new PostRequest(refInstancesRecord1Url, + jsonString, APPLICATION_JSON), 200); + +// System.out.println(rsp.getContentAsString()); + + // Now retrieve the applied references from the REST API + // 1. references on the 'from' record. + rsp = sendRequest(new GetRequest(refInstancesRecord1Url), 200); + + String contentAsString = rsp.getContentAsString(); +// System.out.println(contentAsString); + + JSONObject jsonRsp = new JSONObject(new JSONTokener(contentAsString)); + + JSONObject dataObj = (JSONObject)jsonRsp.get("data"); + assertNotNull("JSON 'data' object was null", dataObj); + + JSONArray customRefsFromArray = (JSONArray)dataObj.get("customReferencesFrom"); + assertNotNull("JSON 'customReferencesFrom' object was null", customRefsFromArray); + + int customRefsCount = customRefsFromArray.length(); + assertTrue("There should be at least one custom reference. Found " + customRefsFromArray, customRefsCount > 0); + + JSONArray customRefsToArray = (JSONArray)dataObj.get("customReferencesTo"); + assertNotNull("JSON 'customReferencesTo' object was null", customRefsToArray); + assertEquals("customReferencesTo wrong length.", 0, customRefsToArray.length()); + + // 2. Back-references on the 'to' record + String node2Url = testRecord2.toString().replace("://", "/"); + String refInstancesRecord2Url = MessageFormat.format(REF_INSTANCES_URL_FORMAT, node2Url); + + rsp = sendRequest(new GetRequest(refInstancesRecord2Url), 200); + + contentAsString = rsp.getContentAsString(); + + jsonRsp = new JSONObject(new JSONTokener(contentAsString)); + + dataObj = (JSONObject)jsonRsp.get("data"); + assertNotNull("JSON 'data' object was null", dataObj); + + customRefsToArray = (JSONArray)dataObj.get("customReferencesTo"); + assertNotNull("JSON 'customReferencesTo' object was null", customRefsToArray); + + customRefsCount = customRefsToArray.length(); + assertTrue("There should be at least one custom reference. Found " + customRefsToArray, customRefsCount > 0); + + customRefsFromArray = (JSONArray)dataObj.get("customReferencesFrom"); + assertNotNull("JSON 'customReferencesFrom' object was null", customRefsFromArray); + assertEquals("customReferencesFrom wrong length.", 0, customRefsFromArray.length()); + + + + // Now to delete a reference instance of each type + String protocol = testRecord2.getStoreRef().getProtocol(); + String identifier = testRecord2.getStoreRef().getIdentifier(); + String recId = testRecord2.getId(); + final String queryFormat = "?st={0}&si={1}&id={2}"; + String urlQueryString = MessageFormat.format(queryFormat, protocol, identifier, recId); + + rsp = sendRequest(new DeleteRequest(refInstancesRecord1Url + "/" + generatedRefIds[1] + urlQueryString), 200); + assertTrue(rsp.getContentAsString().contains("success")); + + rsp = sendRequest(new DeleteRequest(refInstancesRecord1Url + "/" + + generatedRefIds[0] + + urlQueryString), 200); + assertTrue(rsp.getContentAsString().contains("success")); + + // Get the reference instances back and confirm they've been removed. + rsp = sendRequest(new GetRequest(refInstancesRecord1Url), 200); + + jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); + + dataObj = (JSONObject)jsonRsp.get("data"); + assertNotNull("JSON 'data' object was null", dataObj); + + customRefsFromArray = (JSONArray)dataObj.get("customReferencesFrom"); + assertNotNull("JSON 'customReferences' object was null", customRefsFromArray); + assertTrue("customRefsArray was unexpectedly not empty.", customRefsFromArray.length() == 0); + } + + public void testMob1630ShouldNotBeAbleToCreateTwoSupersedesReferencesOnOneRecordPair() throws Exception + { + // Create 2 test records. + NodeRef testRecord1 = utils.createRecord(recordFolder, "testRecord1" + System.currentTimeMillis(), "The from recørd"); + NodeRef testRecord2 = utils.createRecord(recordFolder, "testRecord2" + System.currentTimeMillis(), "The to récord"); + + String node1Url = testRecord1.toString().replace("://", "/"); + String node2Url = testRecord2.toString().replace("://", "/"); + String refInstancesRecord1Url = MessageFormat.format(REF_INSTANCES_URL_FORMAT, node1Url); + String refInstancesRecord2Url = MessageFormat.format(REF_INSTANCES_URL_FORMAT, node2Url); + + {// Sanity check. There should be no references defined on these new records. + Response rsp = sendRequest(new GetRequest(refInstancesRecord1Url), 200); + + String rspContent = rsp.getContentAsString(); + JSONObject jsonRsp = new JSONObject(new JSONTokener(rspContent)); + JSONObject dataObj = jsonRsp.getJSONObject("data"); + JSONArray refsFrom = dataObj.getJSONArray("customReferencesFrom"); + JSONArray refsTo = dataObj.getJSONArray("customReferencesTo"); + assertEquals("Incorrect from-refs count.", 0, refsFrom.length()); + assertEquals("Incorrect to-refs count.", 0, refsTo.length()); + } + + // Add a supersedes ref instance between them + final String supersedesRefLocalName = "supersedes"; + String jsonString = new JSONStringer().object() + .key("toNode").value(testRecord2.toString()) + .key("refId").value(supersedesRefLocalName) + .endObject() + .toString(); + + Response rsp = sendRequest(new PostRequest(refInstancesRecord1Url, + jsonString, APPLICATION_JSON), 200); + + // The bug is that we can apply two such references which should not be allowed + rsp = sendRequest(new PostRequest(refInstancesRecord1Url, + jsonString, APPLICATION_JSON), 500); + + {// Retrieve reference instances on this pair of records. + // The first record + rsp = sendRequest(new GetRequest(refInstancesRecord1Url), 200); + + String rspContent = rsp.getContentAsString(); + JSONObject jsonRsp = new JSONObject(new JSONTokener(rspContent)); + JSONObject dataObj = jsonRsp.getJSONObject("data"); + JSONArray refsFrom = dataObj.getJSONArray("customReferencesFrom"); + JSONArray refsTo = dataObj.getJSONArray("customReferencesTo"); + assertEquals("Incorrect from-refs count.", 1, refsFrom.length()); + assertEquals("Incorrect to-refs count.", 0, refsTo.length()); + + // The second record - the back-reference + rsp = sendRequest(new GetRequest(refInstancesRecord2Url), 200); + + rspContent = rsp.getContentAsString(); + jsonRsp = new JSONObject(new JSONTokener(rspContent)); + dataObj = jsonRsp.getJSONObject("data"); + refsFrom = dataObj.getJSONArray("customReferencesFrom"); + refsTo = dataObj.getJSONArray("customReferencesTo"); + assertEquals("Incorrect from-refs count.", 0, refsFrom.length()); + assertEquals("Incorrect to-refs count.", 1, refsTo.length()); + } + + // Delete the reference instance + String protocol = testRecord2.getStoreRef().getProtocol(); + String identifier = testRecord2.getStoreRef().getIdentifier(); + String recId = testRecord2.getId(); + final String queryFormat = "?st={0}&si={1}&id={2}"; + String urlQueryString = MessageFormat.format(queryFormat, protocol, identifier, recId); + + rsp = sendRequest(new DeleteRequest(refInstancesRecord1Url + "/" + supersedesRefLocalName + urlQueryString), 200); + assertTrue(rsp.getContentAsString().contains("success")); + + {// Retrieve reference instances on this pair of records. + // The first record + rsp = sendRequest(new GetRequest(refInstancesRecord1Url), 200); + + String rspContent = rsp.getContentAsString(); + JSONObject jsonRsp = new JSONObject(new JSONTokener(rspContent)); + JSONObject dataObj = jsonRsp.getJSONObject("data"); + JSONArray refsFrom = dataObj.getJSONArray("customReferencesFrom"); + JSONArray refsTo = dataObj.getJSONArray("customReferencesTo"); + assertEquals("Incorrect from-refs count.", 0, refsFrom.length()); + assertEquals("Incorrect to-refs count.", 0, refsTo.length()); + + // The second record - the back-reference + rsp = sendRequest(new GetRequest(refInstancesRecord2Url), 200); + + rspContent = rsp.getContentAsString(); + jsonRsp = new JSONObject(new JSONTokener(rspContent)); + dataObj = jsonRsp.getJSONObject("data"); + refsFrom = dataObj.getJSONArray("customReferencesFrom"); + refsTo = dataObj.getJSONArray("customReferencesTo"); + assertEquals("Incorrect from-refs count.", 0, refsFrom.length()); + assertEquals("Incorrect to-refs count.", 0, refsTo.length()); + } + } + + public void testPostCustomPropertyDefinition() throws Exception + { + long currentTimeMillis = System.currentTimeMillis(); + + // Create one with no propId - it'll get generated. + postCustomPropertyDefinition("customProperty" + currentTimeMillis, null); + + // Create another with an explicit propId. + postCustomPropertyDefinition("customProperty" + currentTimeMillis, "prop" + currentTimeMillis); + } + + /** + * Creates a new property definition using a POST call. + * GETs the resultant property definition. + * + * @param propertyLabel the label to use + * @param propId the propId to use - null to have one generated. + * @return the propId of the new property definition + */ + private String postCustomPropertyDefinition(String propertyLabel, String propId) throws JSONException, + IOException, UnsupportedEncodingException + { + String jsonString; + if (propId == null) + { + jsonString = new JSONStringer().object() + .key("label").value(propertyLabel) + .key("description").value("Dynamically defined test property") + .key("mandatory").value(false) + .key("dataType").value("d:text") + .key("element").value("record") + .key("constraintRef").value("rmc:smList") + // Note no propId + .endObject() + .toString(); + } + else + { + jsonString = new JSONStringer().object() + .key("label").value(propertyLabel) + .key("description").value("Dynamically defined test property") + .key("mandatory").value(false) + .key("dataType").value("d:text") + .key("element").value("record") + .key("constraintRef").value("rmc:smList") + .key("propId").value(propId) + .endObject() + .toString(); + } + + // Submit the JSON request. + final int expectedStatus = 200; + Response rsp = sendRequest(new PostRequest("/api/rma/admin/custompropertydefinitions?element=record", + jsonString, APPLICATION_JSON), expectedStatus); + + String rspContent = rsp.getContentAsString(); + +// System.out.println(rspContent); + + JSONObject jsonRsp = new JSONObject(new JSONTokener(rspContent)); + String urlOfNewPropDef = jsonRsp.getString("url"); + String newPropId = jsonRsp.getString("propId"); + + assertNotNull("urlOfNewPropDef was null.", urlOfNewPropDef); + + // GET from the URL we're given to ensure it's valid + rsp = sendRequest(new GetRequest(urlOfNewPropDef), 200); + rspContent = rsp.getContentAsString(); + +// System.out.println(rspContent); + + jsonRsp = new JSONObject(new JSONTokener(rspContent)); + JSONObject dataObject = jsonRsp.getJSONObject("data"); + assertNotNull("JSON data object was null", dataObject); + JSONObject customPropsObject = dataObject.getJSONObject("customProperties"); + assertNotNull("JSON customProperties object was null", customPropsObject); + assertEquals("Wrong customProperties length.", 1, customPropsObject.length()); + + Object keyToSoleProp = customPropsObject.keys().next(); + +// System.out.println("New property defn: " + keyToSoleProp); + + JSONObject newPropObject = customPropsObject.getJSONObject((String)keyToSoleProp); + assertEquals("Wrong property label.", propertyLabel, newPropObject.getString("label")); + + return newPropId; + } + + public void testPutCustomReferenceDefinition() throws Exception + { + String[] generatedRefIds = postCustomReferenceDefinitions(); + final String pcRefId = generatedRefIds[0]; + final String bidiRefId = generatedRefIds[1]; + + // GET the custom refs in order to retrieve the label/source/target + String refDefnUrl = "/api/rma/admin/customreferencedefinitions/" + bidiRefId; + Response rsp = sendRequest(new GetRequest(refDefnUrl), 200); + + String rspContent = rsp.getContentAsString(); +// System.out.println(rspContent); + JSONObject jsonRsp = new JSONObject(new JSONTokener(rspContent)); + + refDefnUrl = "/api/rma/admin/customreferencedefinitions/" + pcRefId; + rsp = sendRequest(new GetRequest(refDefnUrl), 200); + + rspContent = rsp.getContentAsString(); +// System.out.println(rspContent); + jsonRsp = new JSONObject(new JSONTokener(rspContent)); + + // Update the bidirectional reference. + final String updatedBiDiLabel = "Updated label üøéîçå"; + String jsonString = new JSONStringer().object() + .key("label").value(updatedBiDiLabel) + .endObject() + .toString(); + + refDefnUrl = "/api/rma/admin/customreferencedefinitions/" + bidiRefId; + rsp = sendRequest(new PutRequest(refDefnUrl, + jsonString, APPLICATION_JSON), 200); + + rspContent = rsp.getContentAsString(); +// System.out.println(rspContent); + + jsonRsp = new JSONObject(new JSONTokener(rspContent)); + String urlOfNewRefDef = jsonRsp.getString("url"); + assertNotNull("urlOfNewRefDef was null.", urlOfNewRefDef); + + // GET the bidi reference to ensure it's valid + rsp = sendRequest(new GetRequest(refDefnUrl), 200); + rspContent = rsp.getContentAsString(); + +// System.out.println(rspContent); + + jsonRsp = new JSONObject(new JSONTokener(rspContent)); + JSONObject dataObject = jsonRsp.getJSONObject("data"); + assertNotNull("JSON data object was null", dataObject); + JSONArray customRefsObject = dataObject.getJSONArray("customReferences"); + assertNotNull("JSON customReferences object was null", customRefsObject); + assertEquals("Wrong customReferences length.", 1, customRefsObject.length()); + + JSONObject newRefObject = customRefsObject.getJSONObject(0); + assertEquals("Wrong property label.", updatedBiDiLabel, newRefObject.getString("label")); + + // Update the parent/child reference. + final String updatedPcSource = "Updated source ∆Ωç√∫"; + final String updatedPcTarget = "Updated target ∆Ωç√∫"; + jsonString = new JSONStringer().object() + .key("source").value(updatedPcSource) + .key("target").value(updatedPcTarget) + .endObject() + .toString(); + + refDefnUrl = "/api/rma/admin/customreferencedefinitions/" + pcRefId; + rsp = sendRequest(new PutRequest(refDefnUrl, + jsonString, APPLICATION_JSON), 200); + + rspContent = rsp.getContentAsString(); +// System.out.println(rspContent); + + jsonRsp = new JSONObject(new JSONTokener(rspContent)); + urlOfNewRefDef = jsonRsp.getString("url"); + assertNotNull("urlOfNewRefDef was null.", urlOfNewRefDef); + + // GET the parent/child reference to ensure it's valid + refDefnUrl = "/api/rma/admin/customreferencedefinitions/" + pcRefId; + + rsp = sendRequest(new GetRequest(refDefnUrl), 200); + rspContent = rsp.getContentAsString(); + +// System.out.println(rspContent); + + jsonRsp = new JSONObject(new JSONTokener(rspContent)); + dataObject = jsonRsp.getJSONObject("data"); + assertNotNull("JSON data object was null", dataObject); + customRefsObject = dataObject.getJSONArray("customReferences"); + assertNotNull("JSON customReferences object was null", customRefsObject); + assertEquals("Wrong customReferences length.", 1, customRefsObject.length()); + + newRefObject = customRefsObject.getJSONObject(0); + assertEquals("Wrong reference source.", updatedPcSource, newRefObject.getString("source")); + assertEquals("Wrong reference target.", updatedPcTarget, newRefObject.getString("target")); + } + + public void testGetCustomProperties() throws Exception + { + getCustomProperties(); + } + + private String getCustomProperties() throws Exception, IOException, + UnsupportedEncodingException, JSONException + { + // Ensure that there is at least one custom property. + this.testPostCustomPropertyDefinition(); + + final int expectedStatus = 200; + Response rsp = sendRequest(new GetRequest("/api/rma/admin/custompropertydefinitions?element=record"), expectedStatus); + + String contentAsString = rsp.getContentAsString(); +// System.out.println(contentAsString); + JSONObject jsonRsp = new JSONObject(new JSONTokener(contentAsString)); + + JSONObject dataObj = (JSONObject)jsonRsp.get("data"); + assertNotNull("JSON 'data' object was null", dataObj); + + JSONObject customPropsObj = (JSONObject)dataObj.get("customProperties"); + assertNotNull("JSON 'customProperties' object was null", customPropsObj); + + final int customPropsCount = customPropsObj.length(); + assertTrue("There should be at least one custom property. Found " + customPropsObj, customPropsCount > 0); + + return contentAsString; + } + + public void testGetRecordMetaDataAspects() throws Exception + { + Response rsp = sendRequest(new GetRequest("/api/rma/recordmetadataaspects"), 200); + String contentAsString = rsp.getContentAsString(); + System.out.println(contentAsString); + JSONObject jsonRsp = new JSONObject(new JSONTokener(contentAsString)); + + JSONObject dataObj = (JSONObject)jsonRsp.get("data"); + assertNotNull("JSON 'data' object was null", dataObj); + + JSONArray aspects = dataObj.getJSONArray("recordMetaDataAspects"); + assertNotNull(aspects); + assertEquals(4, aspects.length()); + + // TODO test the items themselves + } + + public void testExport() throws Exception + { + String exportUrl = "/api/rma/admin/export"; + + // define JSON POST body + JSONObject jsonPostData = new JSONObject(); + JSONArray nodeRefs = new JSONArray(); + nodeRefs.put(recordFolder.toString()); + nodeRefs.put(recordFolder2.toString()); + jsonPostData.put("nodeRefs", nodeRefs); + String jsonPostString = jsonPostData.toString(); + + // make the export request + Response rsp = sendRequest(new PostRequest(exportUrl, jsonPostString, APPLICATION_JSON), 200); + assertEquals("application/acp", rsp.getContentType()); + } + + public void testExportInTransferFormat() throws Exception + { + String exportUrl = "/api/rma/admin/export"; + + // define JSON POST body + JSONObject jsonPostData = new JSONObject(); + JSONArray nodeRefs = new JSONArray(); + nodeRefs.put(recordFolder.toString()); + nodeRefs.put(recordFolder2.toString()); + jsonPostData.put("nodeRefs", nodeRefs); + jsonPostData.put("transferFormat", true); + String jsonPostString = jsonPostData.toString(); + + // make the export request + Response rsp = sendRequest(new PostRequest(exportUrl, jsonPostString, APPLICATION_JSON), 200); + assertEquals("application/zip", rsp.getContentType()); + } + + public void testAudit() throws Exception + { + // call the list service to get audit events + Response rsp = sendRequest(new GetRequest(GET_LIST_URL), 200); + //System.out.println("GET : " + rsp.getContentAsString()); + assertEquals("application/json;charset=UTF-8", rsp.getContentType()); + + // get response as JSON and check + JSONObject jsonParsedObject = new JSONObject(new JSONTokener(rsp.getContentAsString())); + assertNotNull(jsonParsedObject); + JSONObject data = jsonParsedObject.getJSONObject("data"); + JSONObject events = data.getJSONObject("auditEvents"); + JSONArray items = events.getJSONArray("items"); + assertEquals(auditService.getAuditEvents().size(), items.length()); + assertTrue(items.length() > 0); + JSONObject item = items.getJSONObject(0); + assertTrue(item.length() == 2); + assertTrue(item.has("label")); + assertTrue(item.has("value")); + + // get the full RM audit log and check response + rsp = sendRequest(new GetRequest(RMA_AUDITLOG_URL), 200); + assertEquals("application/json", rsp.getContentType()); + JSONObject jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); + + // get the full RM audit log as an HTML report and check response + rsp = sendRequest(new GetRequest(RMA_AUDITLOG_URL + "?format=html"), 200); + assertEquals("text/html", rsp.getContentType()); + + // export the full RM audit log and check response + rsp = sendRequest(new GetRequest(RMA_AUDITLOG_URL + "?export=true"), 200); + assertEquals("application/json", rsp.getContentType()); + jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); + + // construct the URL + String nodeUrl = recordCategory.toString().replace("://", "/"); + String auditUrl = MessageFormat.format(GET_NODE_AUDITLOG_URL_FORMAT, nodeUrl); + + // send request + rsp = sendRequest(new GetRequest(auditUrl), 200); + // check response + assertEquals("application/json", rsp.getContentType()); + jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); + + // get the audit log with all restrictions in place + String filteredAuditUrl = auditUrl + "?user=gavinc&size=5&from=2009-01-01&to=2009-12-31&event=Login"; + rsp = sendRequest(new GetRequest(filteredAuditUrl), 200); + // check response + assertEquals("application/json", rsp.getContentType()); + jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); + + // attempt to get the audit log with invalid restrictions in place + filteredAuditUrl = auditUrl + "?user=fred&size=abc&from=2009&to=2010&property=wrong"; + rsp = sendRequest(new GetRequest(filteredAuditUrl), 200); + assertEquals("application/json", rsp.getContentType()); + jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); + + checkAuditStatus(false); + + // start the RM audit log + JSONObject jsonPostData = new JSONObject(); + jsonPostData.put("enabled", true); + String jsonPostString = jsonPostData.toString(); + rsp = sendRequest(new PutRequest(RMA_AUDITLOG_URL, jsonPostString, APPLICATION_JSON), 200); + + checkAuditStatus(true); + + // check the response + //System.out.println(rsp.getContentAsString()); + jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); + JSONObject dataObj = (JSONObject)jsonRsp.get("data"); + assertNotNull("JSON 'data' object was null", dataObj); + assertTrue(dataObj.getBoolean("enabled")); + assertTrue(dataObj.has("started")); + assertTrue(dataObj.has("stopped")); + + // stop the RM audit log + jsonPostData = new JSONObject(); + jsonPostData.put("enabled", false); + jsonPostString = jsonPostData.toString(); + rsp = sendRequest(new PutRequest(RMA_AUDITLOG_URL, jsonPostString, APPLICATION_JSON), 200); + + checkAuditStatus(false); + + // check the response + //System.out.println(rsp.getContentAsString()); + jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); + dataObj = (JSONObject)jsonRsp.get("data"); + assertNotNull("JSON 'data' object was null", dataObj); + assertFalse(dataObj.getBoolean("enabled")); + + // clear the RM audit log + rsp = sendRequest(new DeleteRequest(RMA_AUDITLOG_URL), 200); + //System.out.println(rsp.getContentAsString()); + jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); + dataObj = (JSONObject)jsonRsp.get("data"); + assertNotNull("JSON 'data' object was null", dataObj); + assertFalse(dataObj.getBoolean("enabled")); + } + + private void checkAuditStatus(boolean expected) throws Exception + { + Response rsp = sendRequest(new GetRequest(RMA_AUDITLOG_STATUS_URL), 200); + JSONObject rspObj = new JSONObject(rsp.getContentAsString()); + JSONObject data = rspObj.getJSONObject("data"); + boolean enabled = data.getBoolean("enabled"); + assertEquals("Audit log status does not match expected status.", expected, enabled); + + } + + public void testFileAuditLogAsRecord() throws Exception + { + // Attempt to store audit log at non existent destination, make sure we get 404 + JSONObject jsonPostData = new JSONObject(); + jsonPostData.put("destination", "workspace://SpacesStore/09ca1e02-1c87-4a53-97e7-xxxxxxxxxxxx"); + String jsonPostString = jsonPostData.toString(); + Response rsp = sendRequest(new PostRequest(RMA_AUDITLOG_URL, jsonPostString, APPLICATION_JSON), 404); + + // Attempt to store audit log at wrong type of destination, make sure we get 400 + jsonPostData = new JSONObject(); + jsonPostData.put("destination", recordCategory.toString()); + jsonPostString = jsonPostData.toString(); + rsp = sendRequest(new PostRequest(RMA_AUDITLOG_URL, jsonPostString, APPLICATION_JSON), 400); + + + // Store the full audit log as a record + jsonPostData = new JSONObject(); + jsonPostData.put("destination", recordFolder2); + jsonPostString = jsonPostData.toString(); + rsp = sendRequest(new PostRequest(RMA_AUDITLOG_URL, jsonPostString, APPLICATION_JSON), 200); + + // check the response + System.out.println(rsp.getContentAsString()); + JSONObject jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); + assertTrue(jsonRsp.has("success")); + assertTrue(jsonRsp.getBoolean("success")); + assertTrue(jsonRsp.has("record")); + assertNotNull(jsonRsp.get("record")); + assertTrue(nodeService.exists(new NodeRef(jsonRsp.getString("record")))); + assertTrue(jsonRsp.has("recordName")); + assertNotNull(jsonRsp.get("recordName")); + assertTrue(jsonRsp.getString("recordName").startsWith("audit_")); + + // Store a filtered audit log as a record + jsonPostData = new JSONObject(); + jsonPostData.put("destination", recordFolder2); + jsonPostData.put("size", "50"); + jsonPostData.put("user", "gavinc"); + jsonPostData.put("event", "Update Metadata"); + jsonPostData.put("property", "{http://www.alfresco.org/model/content/1.0}modified"); + jsonPostString = jsonPostData.toString(); + rsp = sendRequest(new PostRequest(RMA_AUDITLOG_URL, jsonPostString, APPLICATION_JSON), 200); + + // check the response + System.out.println(rsp.getContentAsString()); + jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); + assertTrue(jsonRsp.has("success")); + assertTrue(jsonRsp.getBoolean("success")); + assertTrue(jsonRsp.has("record")); + assertNotNull(jsonRsp.get("record")); + assertTrue(nodeService.exists(new NodeRef(jsonRsp.getString("record")))); + assertTrue(jsonRsp.has("recordName")); + assertNotNull(jsonRsp.get("recordName")); + assertTrue(jsonRsp.getString("recordName").startsWith("audit_")); + } + + public void testPropertyLabelWithAccentedChars() throws Exception + { + final long number = System.currentTimeMillis(); + + // Create a property with a simple name + final String simplePropId = "simpleId" + number; + postCustomPropertyDefinition("simple", simplePropId); + + // Create a property whose name has accented chars + final String originalAccentedLabel = "øoê≈çœ"; + final String accentedPropId = "accentedId" + number; + postCustomPropertyDefinition(originalAccentedLabel, accentedPropId); + + // We'll update the label on the simple-name property a few times. + // This will cause the repeated read and write of the entire RM custom model xml file + // This should also leave the accented-char property unchanged. + putCustomPropDefinition("one", simplePropId); + putCustomPropDefinition("two", simplePropId); + putCustomPropDefinition("three", simplePropId); + putCustomPropDefinition("four", simplePropId); + putCustomPropDefinition("five", simplePropId); + + // Now get all the custom properties back. + String rspContent = getCustomProperties(); + + JSONObject rspObject = new JSONObject(new JSONTokener(rspContent)); + JSONObject dataObj = rspObject.getJSONObject("data"); + assertNotNull("jsonObject was null", dataObj); + + JSONObject customPropertiesObj = dataObj.getJSONObject("customProperties"); + assertNotNull("customPropertiesObj was null", customPropertiesObj); + + JSONObject accentedPropertyObj = customPropertiesObj.getJSONObject(RecordsManagementCustomModel.RM_CUSTOM_PREFIX + + ":" + accentedPropId); + assertNotNull("accentedPropertyObj was null", accentedPropertyObj); + + String labelObj = accentedPropertyObj.getString("label"); + assertEquals("labelObj was changed.", originalAccentedLabel, labelObj); + } + + private void putCustomPropDefinition(String label, String id) throws JSONException, IOException, + UnsupportedEncodingException + { + String jsonString = new JSONStringer().object() + .key("label").value(label) + .endObject() + .toString(); + + String propDefnUrl = "/api/rma/admin/custompropertydefinitions/" + id; + Response rsp = sendRequest(new PutRequest(propDefnUrl, + jsonString, APPLICATION_JSON), 200); + + String rspContent = rsp.getContentAsString(); +// System.out.println(rspContent); + + JSONObject jsonRsp = new JSONObject(new JSONTokener(rspContent)); + String urlOfNewPropDef = jsonRsp.getString("url"); + assertNotNull("urlOfNewPropDef was null.", urlOfNewPropDef); + } +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/webscript/RoleRestApiTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/webscript/RoleRestApiTest.java new file mode 100644 index 0000000000..6130973037 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/webscript/RoleRestApiTest.java @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.webscript; + +import java.util.HashSet; +import java.util.Set; + +import org.alfresco.module.org_alfresco_module_rm.capability.Capability; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMWebScriptTestCase; +import org.alfresco.util.GUID; +import org.json.JSONArray; +import org.json.JSONObject; +import org.springframework.extensions.webscripts.TestWebScriptServer.DeleteRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.PostRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.PutRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.Response; + +/** + * This class tests the Rest API for disposition related operations + * + * @author Roy Wetherall + */ +public class RoleRestApiTest extends BaseRMWebScriptTestCase implements RecordsManagementModel +{ + protected static final String GET_ROLES_URL = "/api/rma/admin/rmroles"; + protected static final String SERVICE_URL_PREFIX = "/alfresco/service"; + protected static final String APPLICATION_JSON = "application/json"; + + public void testGetRoles() throws Exception + { + String role1 = GUID.generate(); + String role2 = GUID.generate(); + + // Create a couple or roles by hand + securityService.createRole(filePlan, role1, "My Test Role", getListOfCapabilities(5)); + securityService.createRole(filePlan, role2, "My Test Role Too", getListOfCapabilities(5)); + + // Add the admin user to one of the roles + securityService.assignRoleToAuthority(filePlan, role1, "admin"); + + try + { + // Get the roles + Response rsp = sendRequest(new GetRequest(GET_ROLES_URL),200); + String rspContent = rsp.getContentAsString(); + + JSONObject obj = new JSONObject(rspContent); + JSONObject roles = obj.getJSONObject("data"); + assertNotNull(roles); + + JSONObject roleObj = roles.getJSONObject(role1); + assertNotNull(roleObj); + assertEquals(role1, roleObj.get("name")); + assertEquals("My Test Role", roleObj.get("displayLabel")); + JSONArray caps = roleObj.getJSONArray("capabilities"); + assertNotNull(caps); + assertEquals(5, caps.length()); + + roleObj = roles.getJSONObject(role2); + assertNotNull(roleObj); + assertEquals(role2, roleObj.get("name")); + assertEquals("My Test Role Too", roleObj.get("displayLabel")); + caps = roleObj.getJSONArray("capabilities"); + assertNotNull(caps); + assertEquals(5, caps.length()); + + // Get the roles for "admin" + rsp = sendRequest(new GetRequest(GET_ROLES_URL + "?user=admin"),200); + rspContent = rsp.getContentAsString(); + + obj = new JSONObject(rspContent); + roles = obj.getJSONObject("data"); + assertNotNull(roles); + + roleObj = roles.getJSONObject(role1); + assertNotNull(roleObj); + assertEquals(role1, roleObj.get("name")); + assertEquals("My Test Role", roleObj.get("displayLabel")); + caps = roleObj.getJSONArray("capabilities"); + assertNotNull(caps); + assertEquals(5, caps.length()); + + assertFalse(roles.has(role2)); + } + finally + { + // Clean up + securityService.deleteRole(filePlan, role1); + securityService.deleteRole(filePlan, role2); + } + + } + + public void testPostRoles() throws Exception + { + Set caps = getListOfCapabilities(5); + JSONArray arrCaps = new JSONArray(); + for (Capability cap : caps) + { + arrCaps.put(cap.getName()); + } + + String roleName = GUID.generate(); + + JSONObject obj = new JSONObject(); + obj.put("name", roleName); + obj.put("displayLabel", "Display Label"); + obj.put("capabilities", arrCaps); + + Response rsp = sendRequest(new PostRequest(GET_ROLES_URL, obj.toString(), APPLICATION_JSON),200); + try + { + String rspContent = rsp.getContentAsString(); + + JSONObject resultObj = new JSONObject(rspContent); + JSONObject roleObj = resultObj.getJSONObject("data"); + assertNotNull(roleObj); + + assertNotNull(roleObj); + assertEquals(roleName, roleObj.get("name")); + assertEquals("Display Label", roleObj.get("displayLabel")); + JSONArray resultCaps = roleObj.getJSONArray("capabilities"); + assertNotNull(resultCaps); + assertEquals(5, resultCaps.length()); + } + finally + { + securityService.deleteRole(filePlan, roleName); + } + + } + + public void testPutRole() throws Exception + { + String role1 = GUID.generate(); + securityService.createRole(filePlan, role1, "My Test Role", getListOfCapabilities(5)); + + try + { + Set caps = getListOfCapabilities(4,8); + JSONArray arrCaps = new JSONArray(); + for (Capability cap : caps) + { + System.out.println(cap.getName()); + arrCaps.put(cap.getName()); + } + + JSONObject obj = new JSONObject(); + obj.put("name", role1); + obj.put("displayLabel", "Changed"); + obj.put("capabilities", arrCaps); + + // Get the roles + Response rsp = sendRequest(new PutRequest(GET_ROLES_URL + "/" + role1, obj.toString(), APPLICATION_JSON),200); + String rspContent = rsp.getContentAsString(); + + JSONObject result = new JSONObject(rspContent); + JSONObject roleObj = result.getJSONObject("data"); + assertNotNull(roleObj); + + assertNotNull(roleObj); + assertEquals(role1, roleObj.get("name")); + assertEquals("Changed", roleObj.get("displayLabel")); + JSONArray bob = roleObj.getJSONArray("capabilities"); + assertNotNull(bob); + assertEquals(4, bob.length()); + + // Bad requests + sendRequest(new PutRequest(GET_ROLES_URL + "/cheese", obj.toString(), APPLICATION_JSON), 404); + } + finally + { + // Clean up + securityService.deleteRole(filePlan, role1); + } + + } + + public void testGetRole() throws Exception + { + String role1 = GUID.generate(); + securityService.createRole(filePlan, role1, "My Test Role", getListOfCapabilities(5)); + + try + { + // Get the roles + Response rsp = sendRequest(new GetRequest(GET_ROLES_URL + "/" + role1),200); + String rspContent = rsp.getContentAsString(); + + JSONObject obj = new JSONObject(rspContent); + JSONObject roleObj = obj.getJSONObject("data"); + assertNotNull(roleObj); + + assertNotNull(roleObj); + assertEquals(role1, roleObj.get("name")); + assertEquals("My Test Role", roleObj.get("displayLabel")); + JSONArray caps = roleObj.getJSONArray("capabilities"); + assertNotNull(caps); + assertEquals(5, caps.length()); + + // Bad requests + sendRequest(new GetRequest(GET_ROLES_URL + "/cheese"), 404); + } + finally + { + // Clean up + securityService.deleteRole(filePlan, role1); + } + + } + + public void testDeleteRole() throws Exception + { + String role1 = GUID.generate(); + assertFalse(securityService.existsRole(filePlan, role1)); + securityService.createRole(filePlan, role1, "My Test Role", getListOfCapabilities(5)); + assertTrue(securityService.existsRole(filePlan, role1)); + sendRequest(new DeleteRequest(GET_ROLES_URL + "/" + role1),200); + assertFalse(securityService.existsRole(filePlan, role1)); + + // Bad request + sendRequest(new DeleteRequest(GET_ROLES_URL + "/cheese"), 404); + } + + private Set getListOfCapabilities(int size) + { + return getListOfCapabilities(size, 0); + } + + private Set getListOfCapabilities(int size, int offset) + { + Set result = new HashSet(size); + Set caps = securityService.getCapabilities(); + int count = 0; + for (Capability cap : caps) + { + if (count < size+offset) + { + if (count >= offset) + { + result.add(cap); + } + } + else + { + break; + } + count ++; + } + return result; + } + +} diff --git a/rm-server/test/resources/test-context.xml b/rm-server/test/resources/test-context.xml new file mode 100644 index 0000000000..56abe6dc78 --- /dev/null +++ b/rm-server/test/resources/test-context.xml @@ -0,0 +1,70 @@ + + + + + + + + + + test-model.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 30 3 * * ? + + + + + + + + + + + + + + + + + + + + + ${spaces.store} + ${spaces.archive.store} + + + + \ No newline at end of file diff --git a/rm-server/test/resources/test-model.xml b/rm-server/test/resources/test-model.xml new file mode 100644 index 0000000000..5bf81115d2 --- /dev/null +++ b/rm-server/test/resources/test-model.xml @@ -0,0 +1,45 @@ + + + + + RM Test Model + Roy Wetherall + 1.0 + + + + + + + + + + + + + + + + + + + + + + cm:content + + + + + + + + + + + rma:recordMetaData + + + + + \ No newline at end of file diff --git a/rm-server/test/resources/testCaveatConfig1.json b/rm-server/test/resources/testCaveatConfig1.json new file mode 100644 index 0000000000..1fac03e309 --- /dev/null +++ b/rm-server/test/resources/testCaveatConfig1.json @@ -0,0 +1,8 @@ +{ + "rmc:prjList": { + "admin" : ["Project A", "Project B", "Project C"] + }, + "rmc:smList": { + "admin" : ["NOFORN", "NOCONTRACT", "FOUO", "FGI"] + } +} diff --git a/rm-server/test/resources/testCaveatConfig2.json b/rm-server/test/resources/testCaveatConfig2.json new file mode 100644 index 0000000000..fec003535f --- /dev/null +++ b/rm-server/test/resources/testCaveatConfig2.json @@ -0,0 +1,27 @@ +{ + "rmc:prjList": { + "dmartinz" : ["Project A", "Project B", "Project C"], + "jrogers" : ["Project A", "Project B"], + "GROUP_Engineering" : ["Project A"], + "dfranco" : ["Project A", "Project C"], + "admin" : ["Project A", "Project B", "Project C"], + "GROUP_Finance" : ["Project C"] + }, + "rmc:smList": { + "jrangel" : ["NOFORN", "NOCONTRACT", "FOUO", "FGI"], + "dmartinz" : ["NOFORN", "NOCONTRACT", "FOUO", "FGI"], + "jrogers" : ["NOFORN", "NOCONTRACT", "FOUO", "FGI"], + "hmcneil" : ["NOFORN", "NOCONTRACT", "FOUO", "FGI"], + "dfranco" : ["NOFORN", "FOUO"], + "gsmith" : ["NOFORN", "FOUO"], + "eharris" : ["NOCONTRACT", "FOUO"], + "bbayless" : ["NOCONTRACT", "FOUO"], + "mhouse" : ["NOCONTRACT", "FOUO"], + "aly" : ["FOUO"], + "dsandy" : ["FGI"], + "driggs" : ["NOFORN"], + "admin" : ["FOUO", "NOFORN"], + "test1" : ["NOCONTRACT"], + "GROUP_test1" : ["NOCONTRACT"] + } +} diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000000..36e36d73b6 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +include 'rm-server', 'rm-share' \ No newline at end of file