mirror of
				https://github.com/Alfresco/alfresco-community-repo.git
				synced 2025-10-29 15:21:53 +00:00 
			
		
		
		
	Compare commits
	
		
			30 Commits
		
	
	
		
			feature/AC
			...
			12.9
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | da6e87e001 | ||
|  | 4197c29fce | ||
|  | 3d4785d5ae | ||
|  | 6c68658476 | ||
|  | 1bd439e0ec | ||
|  | 72f84ee50b | ||
|  | 50f26b9137 | ||
|  | b6b1cc3ea0 | ||
|  | 0f1c1cdba1 | ||
|  | 841bc6844e | ||
|  | a5bdf47f00 | ||
|  | a0b279d1ff | ||
|  | fb967dfa9e | ||
|  | f0a51e1347 | ||
|  | 42d56f9d20 | ||
|  | 3f31e4b1a2 | ||
|  | 787a331869 | ||
|  | 5ce3a3ddd6 | ||
|  | 9ed96ec593 | ||
|  | 03b1fa8b09 | ||
|  | d69f9b52c3 | ||
|  | 72b910bb48 | ||
|  | cfaf3b280b | ||
|  | 00d814ec55 | ||
|  | becabb3a41 | ||
|  | 033157800b | ||
|  | b9c8ff91e4 | ||
|  | c54d46ab67 | ||
|  | dec514c5c2 | ||
|  | 7c5a8a1963 | 
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-community-repo-amps</artifactId> | ||||
|       <version>11.141-SNAPSHOT</version> | ||||
|       <version>12.9</version> | ||||
|    </parent> | ||||
|  | ||||
|    <modules> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-governance-services-community-parent</artifactId> | ||||
|       <version>11.141-SNAPSHOT</version> | ||||
|       <version>12.9</version> | ||||
|    </parent> | ||||
|  | ||||
|    <modules> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-governance-services-automation-community-repo</artifactId> | ||||
|       <version>11.141-SNAPSHOT</version> | ||||
|       <version>12.9</version> | ||||
|    </parent> | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -619,11 +619,27 @@ public class BaseRMRestTest extends RestTest | ||||
|      * @return | ||||
|      */ | ||||
|     public List<String> searchForContentAsUser(UserModel user, String term) | ||||
|     { | ||||
|         String query = "cm:name:*" + term + "*"; | ||||
|         return searchForContentAsUser(user,query,"afts"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns search results for the given search term | ||||
|      * | ||||
|      * @param user | ||||
|      * @param term | ||||
|      * @param query language | ||||
|      * @return | ||||
|      * @throws Exception | ||||
|      */ | ||||
|     public List<String> searchForContentAsUser(UserModel user, String q, String queryLanguage) | ||||
|     { | ||||
|         getRestAPIFactory().getRmRestWrapper().authenticateUser(user); | ||||
|         RestRequestQueryModel queryReq = new RestRequestQueryModel(); | ||||
|         SearchRequest query = new SearchRequest(queryReq); | ||||
|         queryReq.setQuery("cm:name:*" + term + "*"); | ||||
|         queryReq.setQuery(q); | ||||
|         queryReq.setLanguage(queryLanguage); | ||||
|  | ||||
|         List<String> names = new ArrayList<>(); | ||||
|         // wait for solr indexing | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-governance-services-community-parent</artifactId> | ||||
|       <version>11.141-SNAPSHOT</version> | ||||
|       <version>12.9</version> | ||||
|    </parent> | ||||
|  | ||||
|    <modules> | ||||
|   | ||||
| @@ -8,7 +8,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-governance-services-community-repo-parent</artifactId> | ||||
|       <version>11.141-SNAPSHOT</version> | ||||
|       <version>12.9</version> | ||||
|    </parent> | ||||
|  | ||||
|    <properties> | ||||
|   | ||||
| @@ -306,7 +306,7 @@ public class RMAfterInvocationProvider extends RMSecurityCommon | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private boolean isUnfiltered(NodeRef nodeRef) | ||||
|     protected boolean isUnfiltered(NodeRef nodeRef) | ||||
|     { | ||||
|         return !nodeService.hasAspect(nodeRef, RecordsManagementModel.ASPECT_FILE_PLAN_COMPONENT); | ||||
|  | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-governance-services-community-repo-parent</artifactId> | ||||
|         <version>11.141-SNAPSHOT</version> | ||||
|         <version>12.9</version> | ||||
|     </parent> | ||||
|  | ||||
|     <build> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>11.141-SNAPSHOT</version> | ||||
|         <version>12.9</version> | ||||
|     </parent> | ||||
|  | ||||
|     <modules> | ||||
|   | ||||
| @@ -8,7 +8,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-amps</artifactId> | ||||
|         <version>11.141-SNAPSHOT</version> | ||||
|         <version>12.9</version> | ||||
|     </parent> | ||||
|  | ||||
|     <properties> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-community-repo</artifactId> | ||||
|       <version>11.141-SNAPSHOT</version> | ||||
|       <version>12.9</version> | ||||
|    </parent> | ||||
|  | ||||
|    <dependencies> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>11.141-SNAPSHOT</version> | ||||
|         <version>12.9</version> | ||||
|     </parent> | ||||
|  | ||||
|     <properties> | ||||
|   | ||||
| @@ -9,6 +9,6 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-packaging</artifactId> | ||||
|         <version>11.141-SNAPSHOT</version> | ||||
|         <version>12.9</version> | ||||
|     </parent> | ||||
| </project> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-packaging</artifactId> | ||||
|         <version>11.141-SNAPSHOT</version> | ||||
|         <version>12.9</version> | ||||
|     </parent> | ||||
|  | ||||
|     <properties> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>11.141-SNAPSHOT</version> | ||||
|         <version>12.9</version> | ||||
|     </parent> | ||||
|  | ||||
|     <modules> | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-packaging</artifactId> | ||||
|         <version>11.141-SNAPSHOT</version> | ||||
|         <version>12.9</version> | ||||
|     </parent> | ||||
|  | ||||
|     <modules> | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-tests</artifactId> | ||||
|         <version>11.141-SNAPSHOT</version> | ||||
|         <version>12.9</version> | ||||
|     </parent> | ||||
|  | ||||
|     <developers> | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-tests</artifactId> | ||||
|         <version>11.141-SNAPSHOT</version> | ||||
|         <version>12.9</version> | ||||
|     </parent> | ||||
|  | ||||
|     <developers> | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-tests</artifactId> | ||||
|         <version>11.141-SNAPSHOT</version> | ||||
|         <version>12.9</version> | ||||
|     </parent> | ||||
|  | ||||
|     <developers> | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-tests</artifactId> | ||||
|         <version>11.141-SNAPSHOT</version> | ||||
|         <version>12.9</version> | ||||
|     </parent> | ||||
|  | ||||
|     <developers> | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-tests</artifactId> | ||||
|         <version>11.141-SNAPSHOT</version> | ||||
|         <version>12.9</version> | ||||
|     </parent> | ||||
|  | ||||
|     <developers> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-packaging</artifactId> | ||||
|         <version>11.141-SNAPSHOT</version> | ||||
|         <version>12.9</version> | ||||
|     </parent> | ||||
|  | ||||
|     <properties> | ||||
|   | ||||
							
								
								
									
										8
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								pom.xml
									
									
									
									
									
								
							| @@ -2,7 +2,7 @@ | ||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||||
|     <modelVersion>4.0.0</modelVersion> | ||||
|     <artifactId>alfresco-community-repo</artifactId> | ||||
|     <version>11.141-SNAPSHOT</version> | ||||
|     <version>12.9</version> | ||||
|     <packaging>pom</packaging> | ||||
|     <name>Alfresco Community Repo Parent</name> | ||||
|  | ||||
| @@ -24,7 +24,7 @@ | ||||
|     <properties> | ||||
|         <acs.version.major>7</acs.version.major> | ||||
|         <acs.version.minor>1</acs.version.minor> | ||||
|         <acs.version.revision>0</acs.version.revision> | ||||
|         <acs.version.revision>1</acs.version.revision> | ||||
|         <acs.version.label /> | ||||
|         <amp.min.version>${acs.version.major}.0.0</amp.min.version> | ||||
|  | ||||
| @@ -61,7 +61,7 @@ | ||||
|         <dependency.jackson-databind.version>2.12.4</dependency.jackson-databind.version> | ||||
|         <dependency.cxf.version>3.4.4</dependency.cxf.version> | ||||
|         <dependency.opencmis.version>1.0.0</dependency.opencmis.version> | ||||
|         <dependency.webscripts.version>8.23</dependency.webscripts.version> | ||||
|         <dependency.webscripts.version>8.26</dependency.webscripts.version> | ||||
|         <dependency.bouncycastle.version>1.69</dependency.bouncycastle.version> | ||||
|         <dependency.mockito-core.version>3.11.2</dependency.mockito-core.version> | ||||
|         <dependency.mockito-all.version>1.10.19</dependency.mockito-all.version> | ||||
| @@ -142,7 +142,7 @@ | ||||
|         <connection>scm:git:https://github.com/Alfresco/alfresco-community-repo.git</connection> | ||||
|         <developerConnection>scm:git:https://github.com/Alfresco/alfresco-community-repo.git</developerConnection> | ||||
|         <url>https://github.com/Alfresco/alfresco-community-repo</url> | ||||
|         <tag>HEAD</tag> | ||||
|         <tag>12.9</tag> | ||||
|     </scm> | ||||
|  | ||||
|     <distributionManagement> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>11.141-SNAPSHOT</version> | ||||
|         <version>12.9</version> | ||||
|     </parent> | ||||
|  | ||||
|     <dependencies> | ||||
|   | ||||
| @@ -30,8 +30,19 @@ import java.net.InetAddress; | ||||
| import java.net.UnknownHostException; | ||||
| import java.util.Arrays; | ||||
| import java.util.Date; | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
| import java.util.Objects; | ||||
| import java.util.OptionalInt; | ||||
| import java.util.function.Function; | ||||
| import java.util.function.Supplier; | ||||
|  | ||||
| import com.google.common.primitives.Ints; | ||||
|  | ||||
| import org.alfresco.repo.bulkimport.BulkFilesystemImporter; | ||||
| import org.alfresco.repo.bulkimport.BulkImportParameters; | ||||
| import org.alfresco.repo.bulkimport.NodeImporter; | ||||
| import org.alfresco.repo.bulkimport.impl.MultiThreadedBulkFilesystemImporter; | ||||
| import org.alfresco.repo.model.Repository; | ||||
| import org.alfresco.service.cmr.model.FileFolderService; | ||||
| import org.alfresco.service.cmr.model.FileInfo; | ||||
| @@ -39,8 +50,12 @@ import org.alfresco.service.cmr.model.FileNotFoundException; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.apache.commons.logging.Log; | ||||
| import org.apache.commons.logging.LogFactory; | ||||
| import org.springframework.extensions.surf.util.I18NUtil; | ||||
| 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; | ||||
|  | ||||
| /** | ||||
|  * contains common fields and methods for the import web scripts. | ||||
| @@ -60,10 +75,10 @@ public class AbstractBulkFileSystemImportWebScript extends DeclarativeWebScript | ||||
|     // Web scripts parameters (common) | ||||
| 	protected static final String PARAMETER_REPLACE_EXISTING        = "replaceExisting"; | ||||
| 	protected static final String PARAMETER_EXISTING_FILE_MODE      = "existingFileMode"; | ||||
| 	protected static final String PARAMETER_VALUE_REPLACE_EXISTING 	= "replaceExisting"; | ||||
| 	protected static final String PARAMETER_VALUE_REPLACE_EXISTING 	= "true"; | ||||
| 	protected static final String PARAMETER_SOURCE_DIRECTORY       	= "sourceDirectory"; | ||||
| 	protected static final String PARAMETER_DISABLE_RULES		    = "disableRules"; | ||||
| 	protected static final String PARAMETER_VALUE_DISABLE_RULES		= "disableRules"; | ||||
| 	protected static final String PARAMETER_VALUE_DISABLE_RULES		= "true"; | ||||
|  | ||||
| 	protected static final String IMPORT_ALREADY_IN_PROGRESS_MODEL_KEY = "importInProgress"; | ||||
| 	protected static final String IMPORT_ALREADY_IN_PROGRESS_ERROR_KEY ="bfsit.error.importAlreadyInProgress"; | ||||
| @@ -75,7 +90,7 @@ public class AbstractBulkFileSystemImportWebScript extends DeclarativeWebScript | ||||
| 	protected Repository repository; | ||||
| 	    | ||||
| 	protected volatile boolean importInProgress; | ||||
|      | ||||
|  | ||||
| 	protected NodeRef getTargetNodeRef(String targetNodeRefStr, String targetPath) throws FileNotFoundException | ||||
| 	{ | ||||
| 		NodeRef targetNodeRef; | ||||
| @@ -219,4 +234,198 @@ public class AbstractBulkFileSystemImportWebScript extends DeclarativeWebScript | ||||
| 		this.repository = repository; | ||||
| 	} | ||||
|  | ||||
| 	protected class MultithreadedImportWebScriptLogic | ||||
| 	{ | ||||
| 		private final MultiThreadedBulkFilesystemImporter bulkImporter; | ||||
| 		private final Supplier<NodeImporter> nodeImporterFactory; | ||||
| 		private final WebScriptRequest request; | ||||
| 		private final Status status; | ||||
| 		private final Cache cache; | ||||
|  | ||||
| 		public MultithreadedImportWebScriptLogic(MultiThreadedBulkFilesystemImporter bulkImporter, Supplier<NodeImporter> nodeImporterFactory, WebScriptRequest request, Status status, Cache cache) | ||||
| 		{ | ||||
| 			this.bulkImporter = Objects.requireNonNull(bulkImporter); | ||||
| 			this.nodeImporterFactory = Objects.requireNonNull(nodeImporterFactory); | ||||
| 			this.request = Objects.requireNonNull(request); | ||||
| 			this.status = Objects.requireNonNull(status); | ||||
| 			this.cache = Objects.requireNonNull(cache); | ||||
| 		} | ||||
|  | ||||
| 		public Map<String, Object> executeImport() | ||||
| 		{ | ||||
| 			Map<String, Object> model  = new HashMap<>(); | ||||
| 			cache.setNeverCache(true); | ||||
| 			String targetPath = null; | ||||
|  | ||||
| 			try | ||||
| 			{ | ||||
| 				targetPath = request.getParameter(PARAMETER_TARGET_PATH); | ||||
| 				if (isRunning()) | ||||
| 				{ | ||||
| 					model.put(IMPORT_ALREADY_IN_PROGRESS_MODEL_KEY, I18NUtil.getMessage(IMPORT_ALREADY_IN_PROGRESS_ERROR_KEY)); | ||||
| 					return model; | ||||
| 				} | ||||
|  | ||||
| 				final BulkImportParameters bulkImportParameters = getBulkImportParameters(); | ||||
| 				final NodeImporter nodeImporter = nodeImporterFactory.get(); | ||||
|  | ||||
| 				bulkImporter.asyncBulkImport(bulkImportParameters, nodeImporter); | ||||
|  | ||||
| 				waitForImportToBegin(); | ||||
|  | ||||
| 				// redirect to the status Web Script | ||||
| 				status.setCode(Status.STATUS_MOVED_TEMPORARILY); | ||||
| 				status.setRedirect(true); | ||||
| 				status.setLocation(request.getServiceContextPath() + WEB_SCRIPT_URI_BULK_FILESYSTEM_IMPORT_STATUS); | ||||
| 			} | ||||
| 			catch (WebScriptException | IllegalArgumentException e) | ||||
| 			{ | ||||
| 				status.setCode(Status.STATUS_BAD_REQUEST, e.getMessage()); | ||||
| 				status.setRedirect(true); | ||||
| 			} | ||||
| 			catch (FileNotFoundException fnfe) | ||||
| 			{ | ||||
| 				status.setCode(Status.STATUS_BAD_REQUEST,"The repository path '" + targetPath + "' does not exist !"); | ||||
| 				status.setRedirect(true); | ||||
| 			} | ||||
| 			catch (Throwable t) | ||||
| 			{ | ||||
| 				throw new WebScriptException(Status.STATUS_INTERNAL_SERVER_ERROR, buildTextMessage(t), t); | ||||
| 			} | ||||
|  | ||||
| 			return model; | ||||
| 		} | ||||
|  | ||||
| 		private void waitForImportToBegin() throws InterruptedException | ||||
| 		{ | ||||
| 			// ACE-3047 fix, since bulk import is started asynchronously there is a chance that client | ||||
| 			// will get into the status page before import is actually started. | ||||
| 			// In this case wrong information (for previous import) will be displayed. | ||||
| 			// So lets ensure that import started before redirecting client to status page. | ||||
| 			int i = 0; | ||||
| 			while (!bulkImporter.getStatus().inProgress() && i < 10) | ||||
| 			{ | ||||
| 				Thread.sleep(100); | ||||
| 				i++; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		private BulkImportParameters getBulkImportParameters() throws FileNotFoundException | ||||
| 		{ | ||||
| 			final BulkImportParametersExtractor extractor = new BulkImportParametersExtractor(request::getParameter, | ||||
| 					AbstractBulkFileSystemImportWebScript.this::getTargetNodeRef, | ||||
| 					bulkImporter.getDefaultBatchSize(), | ||||
| 					bulkImporter.getDefaultNumThreads()); | ||||
| 			return extractor.extract(); | ||||
| 		} | ||||
|  | ||||
| 		private boolean isRunning() | ||||
| 		{ | ||||
| 			return bulkImporter.getStatus().inProgress(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	protected static class BulkImportParametersExtractor | ||||
| 	{ | ||||
| 		private final Function<String, String> paramsProvider; | ||||
| 		private final NodeRefCreator nodeRefCreator; | ||||
| 		private final int defaultBatchSize; | ||||
| 		private final int defaultNumThreads; | ||||
|  | ||||
| 		public BulkImportParametersExtractor(final Function<String, String> paramsProvider, final NodeRefCreator nodeRefCreator, | ||||
| 											 final int defaultBatchSize, final int defaultNumThreads) | ||||
| 		{ | ||||
| 			this.paramsProvider = Objects.requireNonNull(paramsProvider); | ||||
| 			this.nodeRefCreator = Objects.requireNonNull(nodeRefCreator); | ||||
| 			this.defaultBatchSize = defaultBatchSize; | ||||
| 			this.defaultNumThreads = defaultNumThreads; | ||||
| 		} | ||||
|  | ||||
| 		public BulkImportParameters extract() throws FileNotFoundException | ||||
| 		{ | ||||
| 			BulkImportParameters result = new BulkImportParameters(); | ||||
|  | ||||
| 			result.setTarget(getTargetNodeRef()); | ||||
| 			setExistingFileMode(result); | ||||
| 			result.setNumThreads(getOptionalPositiveInteger(PARAMETER_NUM_THREADS).orElse(defaultNumThreads)); | ||||
| 			result.setBatchSize(getOptionalPositiveInteger(PARAMETER_BATCH_SIZE).orElse(defaultBatchSize)); | ||||
| 			setDisableRules(result); | ||||
|  | ||||
| 			return result; | ||||
| 		} | ||||
|  | ||||
| 		private void setExistingFileMode(BulkImportParameters params) | ||||
| 		{ | ||||
| 			String replaceExistingStr = getParamStringValue(PARAMETER_REPLACE_EXISTING); | ||||
| 			String existingFileModeStr = getParamStringValue(PARAMETER_EXISTING_FILE_MODE); | ||||
|  | ||||
| 			if (!isNullOrEmpty(replaceExistingStr) && !isNullOrEmpty(existingFileModeStr)) | ||||
| 			{ | ||||
| 				// Check that we haven't had both the deprecated and new (existingFileMode) | ||||
| 				// parameters supplied. | ||||
| 				throw new IllegalStateException( | ||||
| 						String.format("Only one of these parameters may be used, not both: %s, %s", | ||||
| 								PARAMETER_REPLACE_EXISTING, | ||||
| 								PARAMETER_EXISTING_FILE_MODE)); | ||||
| 			} | ||||
|  | ||||
| 			if (!isNullOrEmpty(existingFileModeStr)) | ||||
| 			{ | ||||
| 				params.setExistingFileMode(BulkImportParameters.ExistingFileMode.valueOf(existingFileModeStr)); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				params.setReplaceExisting(PARAMETER_VALUE_REPLACE_EXISTING.equals(replaceExistingStr)); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		private void setDisableRules(final BulkImportParameters params) | ||||
| 		{ | ||||
| 			final String disableRulesStr = getParamStringValue(PARAMETER_DISABLE_RULES); | ||||
| 			params.setDisableRulesService(!isNullOrEmpty(disableRulesStr) && PARAMETER_VALUE_DISABLE_RULES.equals(disableRulesStr)); | ||||
| 		} | ||||
|  | ||||
| 		private NodeRef getTargetNodeRef() throws FileNotFoundException | ||||
| 		{ | ||||
| 			String targetNodeRefStr = getParamStringValue(PARAMETER_TARGET_NODEREF); | ||||
| 			String targetPath = getParamStringValue(PARAMETER_TARGET_PATH); | ||||
| 			return nodeRefCreator.fromNodeRefAndPath(targetNodeRefStr, targetPath); | ||||
| 		} | ||||
|  | ||||
| 		private OptionalInt getOptionalPositiveInteger(final String paramName) | ||||
| 		{ | ||||
| 			final String strValue = getParamStringValue(paramName); | ||||
| 			if (isNullOrEmpty(strValue)) | ||||
| 			{ | ||||
| 				return OptionalInt.empty(); | ||||
| 			} | ||||
|  | ||||
| 			final Integer asInt = Ints.tryParse(strValue); | ||||
| 			if (asInt == null || asInt < 1) | ||||
| 			{ | ||||
| 				throw new WebScriptException("Error: parameter '" + paramName + "' must be an integer > 0."); | ||||
| 			} | ||||
|  | ||||
| 			return OptionalInt.of(asInt); | ||||
| 		} | ||||
|  | ||||
| 		private String getParamStringValue(String paramName) | ||||
| 		{ | ||||
| 			Objects.requireNonNull(paramName); | ||||
|  | ||||
| 			return paramsProvider.apply(paramName); | ||||
| 		} | ||||
|  | ||||
| 		private boolean isNullOrEmpty(String str) | ||||
| 		{ | ||||
| 			return str == null || str.trim().length() == 0; | ||||
| 		} | ||||
|  | ||||
| 		@FunctionalInterface | ||||
| 		protected interface NodeRefCreator | ||||
| 		{ | ||||
| 			NodeRef fromNodeRefAndPath(String nodeRef, String path) throws FileNotFoundException; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
| @@ -27,17 +27,12 @@ | ||||
| package org.alfresco.repo.web.scripts.bulkimport.copy; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
|  | ||||
| import org.alfresco.repo.bulkimport.BulkImportParameters; | ||||
| import org.alfresco.repo.bulkimport.NodeImporter; | ||||
| import org.alfresco.repo.bulkimport.impl.MultiThreadedBulkFilesystemImporter; | ||||
| import org.alfresco.repo.bulkimport.impl.StreamingNodeImporterFactory; | ||||
| import org.alfresco.repo.web.scripts.bulkimport.AbstractBulkFileSystemImportWebScript; | ||||
| import org.alfresco.service.cmr.model.FileNotFoundException; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.springframework.extensions.surf.util.I18NUtil; | ||||
| import org.springframework.extensions.webscripts.Cache; | ||||
| import org.springframework.extensions.webscripts.Status; | ||||
| import org.springframework.extensions.webscripts.WebScriptException; | ||||
| @@ -69,170 +64,22 @@ public class BulkFilesystemImportWebScript extends AbstractBulkFileSystemImportW | ||||
|     @Override | ||||
|     protected Map<String, Object> executeImpl(final WebScriptRequest request, final Status status, final Cache cache) | ||||
|     { | ||||
|         Map<String, Object> model  = new HashMap<String, Object>(); | ||||
|         String targetNodeRefStr = null; | ||||
|         String targetPath = null; | ||||
|         String sourceDirectoryStr = null; | ||||
|         @Deprecated String replaceExistingStr = null; | ||||
|         String existingFileModeStr = null; | ||||
|         String batchSizeStr = null; | ||||
|         String numThreadsStr = null; | ||||
|         String disableRulesStr = null; | ||||
|         final MultithreadedImportWebScriptLogic importLogic = new MultithreadedImportWebScriptLogic(bulkImporter, | ||||
|                 () -> createNodeImporter(request), request, status, cache); | ||||
|         return importLogic.executeImport(); | ||||
|     } | ||||
|  | ||||
|         cache.setNeverCache(true); | ||||
|          | ||||
|         try | ||||
|     private NodeImporter createNodeImporter(WebScriptRequest request) | ||||
|     { | ||||
|         final String sourceDirectoryStr = request.getParameter(PARAMETER_SOURCE_DIRECTORY); | ||||
|         if (sourceDirectoryStr == null || sourceDirectoryStr.trim().length() == 0) | ||||
|         { | ||||
|             if(!bulkImporter.getStatus().inProgress()) | ||||
|             { | ||||
|                 NodeRef targetNodeRef = null; | ||||
|                 File sourceDirectory = null; | ||||
|                 boolean replaceExisting = false; | ||||
|                 BulkImportParameters.ExistingFileMode existingFileMode = null; | ||||
|                 int batchSize = bulkImporter.getDefaultBatchSize(); | ||||
|                 int numThreads = bulkImporter.getDefaultNumThreads(); | ||||
|                 boolean disableRules = false; | ||||
|                  | ||||
|                 // Retrieve, validate and convert parameters | ||||
|                 targetNodeRefStr = request.getParameter(PARAMETER_TARGET_NODEREF); | ||||
|                 targetPath = request.getParameter(PARAMETER_TARGET_PATH); | ||||
|                 sourceDirectoryStr = request.getParameter(PARAMETER_SOURCE_DIRECTORY); | ||||
|                 replaceExistingStr = request.getParameter(PARAMETER_REPLACE_EXISTING); | ||||
|                 existingFileModeStr = request.getParameter(PARAMETER_EXISTING_FILE_MODE); | ||||
|  | ||||
|                 batchSizeStr = request.getParameter(PARAMETER_BATCH_SIZE); | ||||
|                 numThreadsStr = request.getParameter(PARAMETER_NUM_THREADS); | ||||
|                 disableRulesStr = request.getParameter(PARAMETER_DISABLE_RULES); | ||||
|  | ||||
|                 targetNodeRef = getTargetNodeRef(targetNodeRefStr, targetPath); | ||||
|                  | ||||
|                 if (sourceDirectoryStr == null || sourceDirectoryStr.trim().length() == 0) | ||||
|                 { | ||||
|                     throw new RuntimeException("Error: mandatory parameter '" + PARAMETER_SOURCE_DIRECTORY + "' was not provided."); | ||||
|                 } | ||||
|                  | ||||
|                 sourceDirectory = new File(sourceDirectoryStr.trim()); | ||||
|  | ||||
|                 if (replaceExistingStr != null && existingFileModeStr != null) | ||||
|                 { | ||||
|                     // Check that we haven't had both the deprecated and new (existingFileMode) | ||||
|                     // parameters supplied. | ||||
|                     throw new IllegalStateException( | ||||
|                             String.format("Only one of these parameters may be used, not both: %s, %s", | ||||
|                                     PARAMETER_REPLACE_EXISTING, | ||||
|                                     PARAMETER_EXISTING_FILE_MODE)); | ||||
|                 } | ||||
|  | ||||
|                 if (replaceExistingStr != null && replaceExistingStr.trim().length() > 0) | ||||
|                 { | ||||
|                     replaceExisting = PARAMETER_VALUE_REPLACE_EXISTING.equals(replaceExistingStr); | ||||
|                 } | ||||
|  | ||||
|                 if (existingFileModeStr != null && existingFileModeStr.trim().length() > 0) | ||||
|                 { | ||||
|                     existingFileMode = BulkImportParameters.ExistingFileMode.valueOf(existingFileModeStr); | ||||
|                 } | ||||
|  | ||||
|                 if (disableRulesStr != null && disableRulesStr.trim().length() > 0) | ||||
|                 { | ||||
|                     disableRules = PARAMETER_VALUE_DISABLE_RULES.equals(disableRulesStr); | ||||
|                 } | ||||
|  | ||||
|                 // Initiate the import | ||||
|                 NodeImporter nodeImporter = nodeImporterFactory.getNodeImporter(sourceDirectory); | ||||
|                 BulkImportParameters bulkImportParameters = new BulkImportParameters(); | ||||
|                  | ||||
|                 if (numThreadsStr != null && numThreadsStr.trim().length() > 0) | ||||
|                 { | ||||
|                 	try | ||||
|                 	{ | ||||
|                 		numThreads = Integer.parseInt(numThreadsStr); | ||||
|                 		if(numThreads < 1) | ||||
|                 		{ | ||||
|                             throw new RuntimeException("Error: parameter '" + PARAMETER_NUM_THREADS + "' must be an integer > 0."); | ||||
|                 		} | ||||
|                         bulkImportParameters.setNumThreads(numThreads); | ||||
|                 	} | ||||
|                 	catch(NumberFormatException e) | ||||
|                 	{ | ||||
|                         throw new RuntimeException("Error: parameter '" + PARAMETER_NUM_THREADS + "' must be an integer > 0."); | ||||
|                 	} | ||||
|                 } | ||||
|                  | ||||
|                 if (batchSizeStr != null && batchSizeStr.trim().length() > 0) | ||||
|                 { | ||||
|                 	try | ||||
|                 	{ | ||||
|                 		batchSize = Integer.parseInt(batchSizeStr); | ||||
|                 		if(batchSize < 1) | ||||
|                 		{ | ||||
|                             throw new RuntimeException("Error: parameter '" + PARAMETER_BATCH_SIZE + "' must be an integer > 0."); | ||||
|                 		} | ||||
|                         bulkImportParameters.setBatchSize(batchSize); | ||||
|                 	} | ||||
|                 	catch(NumberFormatException e) | ||||
|                 	{ | ||||
|                         throw new RuntimeException("Error: parameter '" + PARAMETER_BATCH_SIZE + "' must be an integer > 0."); | ||||
|                 	} | ||||
|                 } | ||||
|  | ||||
|                 if (existingFileMode != null) | ||||
|                 { | ||||
|                     bulkImportParameters.setExistingFileMode(existingFileMode); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     // Fall back to the old/deprecated way. | ||||
|                     bulkImportParameters.setReplaceExisting(replaceExisting); | ||||
|                 } | ||||
|  | ||||
|                 bulkImportParameters.setTarget(targetNodeRef); | ||||
|                 bulkImportParameters.setDisableRulesService(disableRules); | ||||
|  | ||||
|                 bulkImporter.asyncBulkImport(bulkImportParameters, nodeImporter); | ||||
|  | ||||
|                 // ACE-3047 fix, since bulk import is started asynchronously there is a chance that client  | ||||
|                 // will get into the status page before import is actually started. | ||||
|                 // In this case wrong information (for previous import) will be displayed. | ||||
|                 // So lets ensure that import started before redirecting client to status page. | ||||
|                 int i = 0; | ||||
|                 while (!bulkImporter.getStatus().inProgress() && i < 10) | ||||
|                 { | ||||
|                 	Thread.sleep(100); | ||||
|                 	i++; | ||||
|                 } | ||||
|                  | ||||
|                 // redirect to the status Web Script | ||||
|                 status.setCode(Status.STATUS_MOVED_TEMPORARILY); | ||||
|                 status.setRedirect(true); | ||||
|                 status.setLocation(request.getServiceContextPath() + WEB_SCRIPT_URI_BULK_FILESYSTEM_IMPORT_STATUS); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|             	model.put(IMPORT_ALREADY_IN_PROGRESS_MODEL_KEY, I18NUtil.getMessage(IMPORT_ALREADY_IN_PROGRESS_ERROR_KEY)); | ||||
|             } | ||||
|             throw new WebScriptException("Error: mandatory parameter '" + PARAMETER_SOURCE_DIRECTORY + "' was not provided."); | ||||
|         } | ||||
|         catch (WebScriptException wse) | ||||
|         { | ||||
|         	status.setCode(Status.STATUS_BAD_REQUEST, wse.getMessage()); | ||||
|         	status.setRedirect(true); | ||||
|         } | ||||
|         catch (FileNotFoundException fnfe) | ||||
|         { | ||||
|         	status.setCode(Status.STATUS_BAD_REQUEST,"The repository path '" + targetPath + "' does not exist !"); | ||||
|         	status.setRedirect(true); | ||||
|         } | ||||
|         catch(IllegalArgumentException iae) | ||||
|         { | ||||
|         	status.setCode(Status.STATUS_BAD_REQUEST,iae.getMessage()); | ||||
|         	status.setRedirect(true); | ||||
|         } | ||||
|         catch (Throwable t) | ||||
|         { | ||||
|             throw new WebScriptException(Status.STATUS_INTERNAL_SERVER_ERROR, buildTextMessage(t), t); | ||||
|         } | ||||
|          | ||||
|         return model; | ||||
|  | ||||
|         final File sourceDirectory = new File(sourceDirectoryStr.trim()); | ||||
|  | ||||
|         return nodeImporterFactory.getNodeImporter(sourceDirectory); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,28 +1,28 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Remote API | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2016 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software.  | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of  | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is  | ||||
|  * provided under the following open source license terms: | ||||
|  *  | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  *  | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  *  | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Remote API | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2016 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software.  | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of  | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is  | ||||
|  * provided under the following open source license terms: | ||||
|  *  | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  *  | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  *  | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.repo.web.scripts.quickshare; | ||||
|  | ||||
| import java.util.HashMap; | ||||
| @@ -31,10 +31,12 @@ import java.util.Map; | ||||
| import javax.servlet.http.HttpServletResponse; | ||||
|  | ||||
| import org.alfresco.model.QuickShareModel; | ||||
| import org.alfresco.repo.tenant.TenantUtil; | ||||
| import org.alfresco.service.cmr.quickshare.InvalidSharedIdException; | ||||
| import org.alfresco.service.cmr.repository.InvalidNodeRefException; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.alfresco.service.cmr.repository.NodeService; | ||||
| import org.alfresco.util.Pair; | ||||
| import org.apache.commons.logging.Log; | ||||
| import org.apache.commons.logging.LogFactory; | ||||
| import org.springframework.extensions.webscripts.Cache; | ||||
| @@ -82,14 +84,15 @@ public class UnshareContentDelete extends AbstractQuickShareContent | ||||
|  | ||||
|         try | ||||
|         { | ||||
|             NodeRef nodeRef = quickShareService.getTenantNodeRefFromSharedId(sharedId).getSecond(); | ||||
|             Pair<String, NodeRef> pair = quickShareService.getTenantNodeRefFromSharedId(sharedId); | ||||
|             String networkTenantDomain = pair.getFirst(); | ||||
|  | ||||
|             String sharedBy = (String) nodeService.getProperty(nodeRef, QuickShareModel.PROP_QSHARE_SHAREDBY); | ||||
|             if (!quickShareService.canDeleteSharedLink(nodeRef, sharedBy)) | ||||
|             TenantUtil.runAsSystemTenant(() -> | ||||
|             { | ||||
|                 throw new WebScriptException(HttpServletResponse.SC_FORBIDDEN, "Can't perform unshare action: " + sharedId); | ||||
|             } | ||||
|             quickShareService.unshareContent(sharedId); | ||||
|                 checkIfCanDeleteSharedLink(sharedId); | ||||
|                 quickShareService.unshareContent(sharedId); | ||||
|                 return null; | ||||
|             }, networkTenantDomain); | ||||
|  | ||||
|             Map<String, Object> model = new HashMap<>(1); | ||||
|             model.put("success", Boolean.TRUE); | ||||
| @@ -106,4 +109,14 @@ public class UnshareContentDelete extends AbstractQuickShareContent | ||||
|             throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, "Unable to find: " + sharedId); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void checkIfCanDeleteSharedLink(String sharedId) { | ||||
|         NodeRef nodeRef = quickShareService.getTenantNodeRefFromSharedId(sharedId).getSecond(); | ||||
|  | ||||
|         String sharedBy = (String) nodeService.getProperty(nodeRef, QuickShareModel.PROP_QSHARE_SHAREDBY); | ||||
|         if (!quickShareService.canDeleteSharedLink(nodeRef, sharedBy)) | ||||
|         { | ||||
|             throw new WebScriptException(HttpServletResponse.SC_FORBIDDEN, "Can't perform unshare action: " + sharedId); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -274,15 +274,15 @@ public class QuickShareLinksImpl implements QuickShareLinks, RecognizedParamsExt | ||||
|  | ||||
|         try | ||||
|         { | ||||
|             NodeRef nodeRef = quickShareService.getTenantNodeRefFromSharedId(sharedId).getSecond(); | ||||
|             Pair<String, NodeRef> pair = quickShareService.getTenantNodeRefFromSharedId(sharedId); | ||||
|             String networkTenantDomain = pair.getFirst(); | ||||
|  | ||||
|             String sharedByUserId = (String)nodeService.getProperty(nodeRef, QuickShareModel.PROP_QSHARE_SHAREDBY); | ||||
|             if (!quickShareService.canDeleteSharedLink(nodeRef, sharedByUserId)) | ||||
|             TenantUtil.runAsSystemTenant(() -> | ||||
|             { | ||||
|                 throw new PermissionDeniedException("Can't perform unshare action: " + sharedId); | ||||
|             } | ||||
|  | ||||
|             quickShareService.unshareContent(sharedId); | ||||
|                 checkIfCanDeleteSharedLink(sharedId); | ||||
|                 quickShareService.unshareContent(sharedId); | ||||
|                 return null; | ||||
|             }, networkTenantDomain); | ||||
|         } | ||||
|         catch (InvalidSharedIdException ex) | ||||
|         { | ||||
| @@ -698,4 +698,14 @@ public class QuickShareLinksImpl implements QuickShareLinks, RecognizedParamsExt | ||||
|             throw new InvalidArgumentException("A valid recipientEmail must be specified."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void checkIfCanDeleteSharedLink(String sharedId) { | ||||
|         NodeRef nodeRef = quickShareService.getTenantNodeRefFromSharedId(sharedId).getSecond(); | ||||
|  | ||||
|         String sharedByUserId = (String)nodeService.getProperty(nodeRef, QuickShareModel.PROP_QSHARE_SHAREDBY); | ||||
|         if (!quickShareService.canDeleteSharedLink(nodeRef, sharedByUserId)) | ||||
|         { | ||||
|             throw new PermissionDeniedException("Can't perform unshare action: " + sharedId); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1070,7 +1070,7 @@ | ||||
|         <property name="enabled" value="${system.api.discovery.enabled}" /> | ||||
|         <property name="thumbnailService" ref="ThumbnailService" /> | ||||
|         <property name="restApiDirectUrlConfig" ref="restApiDirectUrlConfig" /> | ||||
|         <property name="contentService" ref="ContentService" /> | ||||
|         <property name="contentService" ref="contentService" /> | ||||
|     </bean> | ||||
|  | ||||
|     <bean id="org.alfresco.rest.api.probes.ProbeEntityResource.get" class="org.alfresco.rest.api.probes.ProbeEntityResource"> | ||||
|   | ||||
| @@ -42,6 +42,7 @@ import org.junit.runners.Suite; | ||||
|     org.alfresco.rest.api.tests.TestPublicApiAtomPub10TCK.class, | ||||
|     org.alfresco.rest.api.tests.TestPublicApiAtomPub11TCK.class, | ||||
|     org.alfresco.rest.api.tests.TestPublicApiBrowser11TCK.class, | ||||
|     org.alfresco.repo.web.scripts.bulkimport.BulkImportParametersExtractorTest.class | ||||
| }) | ||||
| public class AppContext01TestSuite | ||||
| { | ||||
|   | ||||
| @@ -0,0 +1,252 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Remote API | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2021 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is | ||||
|  * provided under the following open source license terms: | ||||
|  * | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.repo.web.scripts.bulkimport; | ||||
|  | ||||
| import static org.alfresco.repo.web.scripts.bulkimport.AbstractBulkFileSystemImportWebScript.PARAMETER_BATCH_SIZE; | ||||
| import static org.alfresco.repo.web.scripts.bulkimport.AbstractBulkFileSystemImportWebScript.PARAMETER_DISABLE_RULES; | ||||
| import static org.alfresco.repo.web.scripts.bulkimport.AbstractBulkFileSystemImportWebScript.PARAMETER_NUM_THREADS; | ||||
| import static org.alfresco.repo.web.scripts.bulkimport.AbstractBulkFileSystemImportWebScript.PARAMETER_TARGET_NODEREF; | ||||
| import static org.junit.Assert.assertEquals; | ||||
| import static org.junit.Assert.assertFalse; | ||||
| import static org.junit.Assert.assertNotNull; | ||||
| import static org.junit.Assert.assertNull; | ||||
| import static org.junit.Assert.assertThrows; | ||||
| import static org.junit.Assert.assertTrue; | ||||
| import static org.junit.Assert.fail; | ||||
|  | ||||
| import java.util.Map; | ||||
|  | ||||
| import org.alfresco.repo.bulkimport.BulkImportParameters; | ||||
| import org.alfresco.repo.bulkimport.BulkImportParameters.ExistingFileMode; | ||||
| import org.alfresco.repo.web.scripts.bulkimport.AbstractBulkFileSystemImportWebScript.BulkImportParametersExtractor; | ||||
| import org.alfresco.service.cmr.model.FileNotFoundException; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.junit.Test; | ||||
| import org.springframework.extensions.webscripts.WebScriptException; | ||||
|  | ||||
| public class BulkImportParametersExtractorTest | ||||
| { | ||||
|     private static final String TEST_NODE_REF = "workspace://SpacesStore/this-is-just-a-test-ref"; | ||||
|     private static final String TEST_MISSING_NODE_REF = "workspace://SpacesStore/this-is-just-a-not-existing-test-ref"; | ||||
|     private static final Integer DEFAULT_BATCH_SIZE = 1234; | ||||
|     private static final Integer DEFAULT_NUMBER_OF_THREADS = 4321; | ||||
|  | ||||
|     @Test | ||||
|     public void shouldExtractTargetRef() throws FileNotFoundException | ||||
|     { | ||||
|         final BulkImportParametersExtractor extractor = givenExtractor(Map.of( | ||||
|                 PARAMETER_TARGET_NODEREF, TEST_NODE_REF)); | ||||
|  | ||||
|         final BulkImportParameters params = extractor.extract(); | ||||
|  | ||||
|         assertNotNull(params); | ||||
|         assertNotNull(params.getTarget()); | ||||
|         assertEquals(TEST_NODE_REF, params.getTarget().toString()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void shouldFallbackToDefaultValues() throws FileNotFoundException | ||||
|     { | ||||
|         final BulkImportParametersExtractor extractor = givenExtractor(Map.of( | ||||
|                 PARAMETER_TARGET_NODEREF, TEST_NODE_REF)); | ||||
|  | ||||
|         final BulkImportParameters params = extractor.extract(); | ||||
|  | ||||
|         assertEquals(DEFAULT_BATCH_SIZE, params.getBatchSize()); | ||||
|         assertEquals(DEFAULT_NUMBER_OF_THREADS, params.getNumThreads()); | ||||
|         assertFalse(params.isDisableRulesService()); | ||||
|         assertEquals(ExistingFileMode.SKIP, params.getExistingFileMode()); | ||||
|         assertNull(params.getLoggingInterval()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void shouldExtractDisableFolderRulesFlagWhenSetToTrue() throws FileNotFoundException | ||||
|     { | ||||
|         final BulkImportParametersExtractor extractor = givenExtractor(Map.of( | ||||
|                 PARAMETER_TARGET_NODEREF, TEST_NODE_REF, | ||||
|                 PARAMETER_DISABLE_RULES, "true" | ||||
|                                                                              )); | ||||
|  | ||||
|         final BulkImportParameters params = extractor.extract(); | ||||
|  | ||||
|         assertTrue(params.isDisableRulesService()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void shouldExtractDisableFolderRulesFlagWhenSetToFalse() throws FileNotFoundException | ||||
|     { | ||||
|         final BulkImportParametersExtractor extractor = givenExtractor(Map.of( | ||||
|                 PARAMETER_TARGET_NODEREF, TEST_NODE_REF, | ||||
|                 PARAMETER_DISABLE_RULES, "false" | ||||
|                                                                              )); | ||||
|  | ||||
|         final BulkImportParameters params = extractor.extract(); | ||||
|  | ||||
|         assertFalse(params.isDisableRulesService()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void shouldExtractDisableFolderRulesFlagWhenSetToNotBooleanValue() throws FileNotFoundException | ||||
|     { | ||||
|         final BulkImportParametersExtractor extractor = givenExtractor(Map.of( | ||||
|                 PARAMETER_TARGET_NODEREF, TEST_NODE_REF, | ||||
|                 PARAMETER_DISABLE_RULES, "unknown" | ||||
|                                                                              )); | ||||
|  | ||||
|         final BulkImportParameters params = extractor.extract(); | ||||
|  | ||||
|         assertFalse(params.isDisableRulesService()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void shouldPropagateFileNotFoundExceptionWhenTargetIsNotFound() | ||||
|     { | ||||
|         final BulkImportParametersExtractor extractor = givenExtractor(Map.of( | ||||
|                 PARAMETER_TARGET_NODEREF, TEST_MISSING_NODE_REF)); | ||||
|  | ||||
|         assertThrows(FileNotFoundException.class, extractor::extract); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void shouldExtractValidBatchSize() throws FileNotFoundException | ||||
|     { | ||||
|         final BulkImportParametersExtractor extractor = givenExtractor(Map.of( | ||||
|                 PARAMETER_TARGET_NODEREF, TEST_NODE_REF, | ||||
|                 PARAMETER_BATCH_SIZE, "1")); | ||||
|  | ||||
|         final BulkImportParameters params = extractor.extract(); | ||||
|  | ||||
|         assertEquals(Integer.valueOf(1), params.getBatchSize()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void shouldFailWithWebScriptExceptionWhenInvalidBatchSizeIsRequested() throws FileNotFoundException | ||||
|     { | ||||
|         final BulkImportParametersExtractor extractor = givenExtractor(Map.of( | ||||
|                 PARAMETER_TARGET_NODEREF, TEST_NODE_REF, | ||||
|                 PARAMETER_BATCH_SIZE, "not-a-number")); | ||||
|  | ||||
|         try | ||||
|         { | ||||
|             extractor.extract(); | ||||
|         } catch (WebScriptException e) | ||||
|         { | ||||
|             assertNotNull(e.getMessage()); | ||||
|             assertTrue(e.getMessage().contains(PARAMETER_BATCH_SIZE)); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         fail("Expected exception to be thrown."); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void shouldFailWithWebScriptExceptionWhenNegativeBatchSizeIsRequested() throws FileNotFoundException | ||||
|     { | ||||
|         final BulkImportParametersExtractor extractor = givenExtractor(Map.of( | ||||
|                 PARAMETER_TARGET_NODEREF, TEST_NODE_REF, | ||||
|                 PARAMETER_BATCH_SIZE, "-1")); | ||||
|  | ||||
|         try | ||||
|         { | ||||
|             extractor.extract(); | ||||
|         } catch (WebScriptException e) | ||||
|         { | ||||
|             assertNotNull(e.getMessage()); | ||||
|             assertTrue(e.getMessage().contains(PARAMETER_BATCH_SIZE)); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         fail("Expected exception to be thrown."); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void shouldExtractValidNumberOfThreads() throws FileNotFoundException | ||||
|     { | ||||
|         final BulkImportParametersExtractor extractor = givenExtractor(Map.of( | ||||
|                 PARAMETER_TARGET_NODEREF, TEST_NODE_REF, | ||||
|                 PARAMETER_NUM_THREADS, "1")); | ||||
|  | ||||
|         final BulkImportParameters params = extractor.extract(); | ||||
|  | ||||
|         assertEquals(Integer.valueOf(1), params.getNumThreads()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void shouldFailWithWebScriptExceptionWhenInvalidNumberOfThreadsIsRequested() throws FileNotFoundException | ||||
|     { | ||||
|         final BulkImportParametersExtractor extractor = givenExtractor(Map.of( | ||||
|                 PARAMETER_TARGET_NODEREF, TEST_NODE_REF, | ||||
|                 PARAMETER_NUM_THREADS, "not-a-number")); | ||||
|  | ||||
|         try | ||||
|         { | ||||
|             extractor.extract(); | ||||
|         } catch (WebScriptException e) | ||||
|         { | ||||
|             assertNotNull(e.getMessage()); | ||||
|             assertTrue(e.getMessage().contains(PARAMETER_NUM_THREADS)); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         fail("Expected exception to be thrown."); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void shouldFailWithWebScriptExceptionWhenNegativeNumberOfThreadsIsRequested() throws FileNotFoundException | ||||
|     { | ||||
|         final BulkImportParametersExtractor extractor = givenExtractor(Map.of( | ||||
|                 PARAMETER_TARGET_NODEREF, TEST_NODE_REF, | ||||
|                 PARAMETER_NUM_THREADS, "-1")); | ||||
|  | ||||
|         try | ||||
|         { | ||||
|             extractor.extract(); | ||||
|         } catch (WebScriptException e) | ||||
|         { | ||||
|             assertNotNull(e.getMessage()); | ||||
|             assertTrue(e.getMessage().contains(PARAMETER_NUM_THREADS)); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         fail("Expected exception to be thrown."); | ||||
|     } | ||||
|  | ||||
|     private BulkImportParametersExtractor givenExtractor(Map<String, String> params) | ||||
|     { | ||||
|  | ||||
|         return new BulkImportParametersExtractor(params::get, this::testRefCreator, DEFAULT_BATCH_SIZE, DEFAULT_NUMBER_OF_THREADS); | ||||
|     } | ||||
|  | ||||
|     private NodeRef testRefCreator(String nodeRef, String path) throws FileNotFoundException | ||||
|     { | ||||
|         if (TEST_MISSING_NODE_REF.equals(nodeRef)) | ||||
|         { | ||||
|             throw new FileNotFoundException(new NodeRef(nodeRef)); | ||||
|         } | ||||
|         return new NodeRef(nodeRef); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>11.141-SNAPSHOT</version> | ||||
|         <version>12.9</version> | ||||
|     </parent> | ||||
|  | ||||
|     <dependencies> | ||||
|   | ||||
| @@ -415,12 +415,13 @@ implements TenantDeployer, DictionaryListener, /*TenantDictionaryListener, */Mes | ||||
|                          | ||||
|                         for (NodeRef messageResource : nodeRefs) | ||||
|                         { | ||||
|                             String resourceName = (String) nodeService.getProperty(messageResource, ContentModel.PROP_NAME); | ||||
|                              | ||||
|                             String bundleBaseName = messageService.getBaseBundleName(resourceName); | ||||
|                             String messageResourcePath = nodeService.getPath(messageResource).toPrefixString(namespaceService); | ||||
|                             String bundleBasePrefixedName = messageResourcePath.substring(messageResourcePath.lastIndexOf("/")); | ||||
|                             String bundleBaseName = messageService.getBaseBundleName(bundleBasePrefixedName);  | ||||
|                              | ||||
|                             if (!resourceBundleBaseNames.contains(bundleBaseName)) | ||||
|                             { | ||||
|                                 messageService.registerResourceBundle(storeRef.toString() + repositoryLocation.getPath() + bundleBaseName); | ||||
|                                 resourceBundleBaseNames.add(bundleBaseName); | ||||
|                             } | ||||
|                         } | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|  * #%L | ||||
|  * Alfresco Repository | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2016 Alfresco Software Limited | ||||
|  * Copyright (C) 2005 - 2021 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software.  | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of  | ||||
| @@ -683,6 +683,7 @@ public class RenditionServiceImpl implements | ||||
|                     log.debug("OnContentUpdate calling RenditionService2.render(\""+sourceNodeRef+"\", \""+renditionName+"\" so we switch to the new service."); | ||||
|                 } | ||||
|                 useRenditionService2 = true; | ||||
|                 renditionService2.clearRenditionContentData(sourceNodeRef, renditionName); | ||||
|                 renditionService2.render(sourceNodeRef, renditionName); | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|  * #%L | ||||
|  * Alfresco Repository | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2020 Alfresco Software Limited | ||||
|  * Copyright (C) 2005 - 2021 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
| @@ -538,9 +538,8 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea | ||||
|                             } | ||||
|                             if (logger.isDebugEnabled()) | ||||
|                             { | ||||
|                                 logger.debug("Set rendition hashcode " + transformContentHashCode + " and ThumbnailLastModified for " + renditionName); | ||||
|                                 logger.debug("Set ThumbnailLastModified for " + renditionName); | ||||
|                             } | ||||
|                             nodeService.setProperty(renditionNode, RenditionModel.PROP_RENDITION_CONTENT_HASH_CODE, transformContentHashCode); | ||||
|                             setThumbnailLastModified(sourceNodeRef, renditionName); | ||||
|  | ||||
|                             if (transformInputStream != null) | ||||
| @@ -554,6 +553,11 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea | ||||
|                                     contentWriter.setEncoding(DEFAULT_ENCODING); | ||||
|                                     ContentWriter renditionWriter = contentWriter; | ||||
|                                     renditionWriter.putContent(transformInputStream); | ||||
|                                     if (logger.isDebugEnabled()) | ||||
|                                     { | ||||
|                                         logger.debug("Set rendition hashcode for " + renditionName); | ||||
|                                     } | ||||
|                                     nodeService.setProperty(renditionNode, RenditionModel.PROP_RENDITION_CONTENT_HASH_CODE, transformContentHashCode); | ||||
|                                 } | ||||
|                                 catch (Exception e) | ||||
|                                 { | ||||
| @@ -563,16 +567,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 Serializable content = nodeService.getProperty(renditionNode, PROP_CONTENT); | ||||
|                                 if (content != null) | ||||
|                                 { | ||||
|                                     nodeService.removeProperty(renditionNode, PROP_CONTENT); | ||||
|                                     nodeService.removeProperty(renditionNode, PROP_RENDITION_CONTENT_HASH_CODE); | ||||
|                                     if (logger.isDebugEnabled()) | ||||
|                                     { | ||||
|                                         logger.debug("Cleared rendition content and hashcode"); | ||||
|                                     } | ||||
|                                 } | ||||
|                                 clearRenditionContentData(renditionNode); | ||||
|                             } | ||||
|  | ||||
|                             if (!sourceHasAspectRenditioned) | ||||
| @@ -736,6 +731,43 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Clears source nodeRef rendition content and content hash code using supplied rendition name | ||||
|      * | ||||
|      * @param sourceNodeRef | ||||
|      * @param renditionName | ||||
|      */ | ||||
|     public void clearRenditionContentData(NodeRef sourceNodeRef, String renditionName) | ||||
|     { | ||||
|         clearRenditionContentData(getRenditionNode(sourceNodeRef, renditionName)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Clears supplied rendition node content (if exists) and content hash code | ||||
|      * | ||||
|      * @param renditionNode | ||||
|      */ | ||||
|     private void clearRenditionContentData(NodeRef renditionNode) | ||||
|     { | ||||
|         if (renditionNode != null) | ||||
|         { | ||||
|             Serializable content = nodeService.getProperty(renditionNode, PROP_CONTENT); | ||||
|             if (content != null) | ||||
|             { | ||||
|                 nodeService.removeProperty(renditionNode, PROP_CONTENT); | ||||
|                 if (logger.isDebugEnabled()) | ||||
|                 { | ||||
|                     logger.debug("Cleared rendition content"); | ||||
|                 } | ||||
|             } | ||||
|             nodeService.removeProperty(renditionNode, PROP_RENDITION_CONTENT_HASH_CODE); | ||||
|             if (logger.isDebugEnabled()) | ||||
|             { | ||||
|                 logger.debug("Cleared rendition hashcode"); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method checks whether the specified source node is of a content class which has been registered for | ||||
|      * rendition prevention. | ||||
| @@ -886,6 +918,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea | ||||
|                         RenditionDefinition2 renditionDefinition = renditionDefinitionRegistry2.getRenditionDefinition(renditionName); | ||||
|                         if (renditionDefinition != null) | ||||
|                         { | ||||
|                             clearRenditionContentData(sourceNodeRef, renditionName); | ||||
|                             render(sourceNodeRef, renditionName); | ||||
|                         } | ||||
|                         else | ||||
|   | ||||
| @@ -1,30 +1,33 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Repository | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2016 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software.  | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of  | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is  | ||||
|  * provided under the following open source license terms: | ||||
|  *  | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  *  | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  *  | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Repository | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2021 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software.  | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of  | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is  | ||||
|  * provided under the following open source license terms: | ||||
|  *  | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  *  | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  *  | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.repo.security.permissions.impl.acegi; | ||||
|  | ||||
| import static org.alfresco.repo.security.permissions.impl.acegi.ACLEntryVoterUtils.getNodeRef; | ||||
| import static org.alfresco.repo.security.permissions.impl.acegi.ACLEntryVoterUtils.shouldAbstainOrDeny; | ||||
|  | ||||
| import java.io.Serializable; | ||||
| import java.lang.reflect.Method; | ||||
| import java.util.ArrayList; | ||||
| @@ -46,8 +49,6 @@ import org.alfresco.repo.security.permissions.impl.SimplePermissionReference; | ||||
| 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.AccessStatus; | ||||
| import org.alfresco.service.cmr.security.AuthenticationService; | ||||
| import org.alfresco.service.cmr.security.AuthorityService; | ||||
| import org.alfresco.service.cmr.security.OwnableService; | ||||
| @@ -59,6 +60,7 @@ import org.apache.commons.logging.Log; | ||||
| import org.apache.commons.logging.LogFactory; | ||||
| import org.springframework.beans.factory.InitializingBean; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * @author andyh | ||||
|  */ | ||||
| @@ -395,61 +397,53 @@ public class ACLEntryVoter implements AccessDecisionVoter, InitializingBean | ||||
|                 { | ||||
|                     throw new ACLEntryVoterException("The specified parameter is not a NodeRef or ChildAssociationRef"); | ||||
|                 } | ||||
|                 else if (StoreRef.class.isAssignableFrom(params[cad.parameter[0]])) | ||||
|  | ||||
|                 if (List.class.isAssignableFrom(params[cad.parameter[0]])) | ||||
|                 { | ||||
|                     StoreRef storeRef = getArgument(invocation, cad.parameter[0]); | ||||
|                     if (storeRef != null) | ||||
|                     List<?> listArgument = getArgument(invocation, cad.parameter[0]); | ||||
|                     if (listArgument != null) | ||||
|                     { | ||||
|                         if (log.isDebugEnabled()) | ||||
|                         NodeRef listNodeRef; | ||||
|                         Integer accessAbstainOrDeny = null; | ||||
|                         for (Object listElement : listArgument) | ||||
|                         { | ||||
|                             log.debug("\tPermission test against the store - using permissions on the root node"); | ||||
|                         } | ||||
|                         if (nodeService.exists(storeRef)) | ||||
|                         { | ||||
|                             testNodeRef = nodeService.getRootNode(storeRef); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 else if (NodeRef.class.isAssignableFrom(params[cad.parameter[0]])) | ||||
|                 { | ||||
|                     testNodeRef = getArgument(invocation, cad.parameter[0]); | ||||
|                     if (log.isDebugEnabled()) | ||||
|                     { | ||||
|                         if (testNodeRef != null) | ||||
|                         { | ||||
|                             if (nodeService.exists(testNodeRef)) | ||||
|                             listNodeRef = getNodeRef(listElement, nodeService); | ||||
|                             Integer currentValue = shouldAbstainOrDeny(cad.required, listNodeRef, abstainForClassQNames, nodeService, permissionService); | ||||
|  | ||||
|                             if (currentValue != null) | ||||
|                             { | ||||
|                                 log.debug("\tPermission test on node " + nodeService.getPath(testNodeRef)); | ||||
|                                 if (currentValue == AccessDecisionVoter.ACCESS_DENIED) | ||||
|                                 { | ||||
|                                     return AccessDecisionVoter.ACCESS_DENIED; | ||||
|                                 } | ||||
|                                 else | ||||
|                                 { | ||||
|                                     accessAbstainOrDeny = currentValue; | ||||
|                                 } | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 log.debug("\tPermission test on non-existing node " +testNodeRef); | ||||
|                             }  | ||||
|  | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 else if (ChildAssociationRef.class.isAssignableFrom(params[cad.parameter[0]])) | ||||
|                 { | ||||
|                     ChildAssociationRef testChildRef = getArgument(invocation, cad.parameter[0]); | ||||
|                     if (testChildRef != null) | ||||
|                     { | ||||
|                         testNodeRef = testChildRef.getChildRef(); | ||||
|                         if (log.isDebugEnabled()) | ||||
|  | ||||
|                         if (accessAbstainOrDeny != null) | ||||
|                         { | ||||
|                             if (nodeService.exists(testNodeRef)) | ||||
|                             { | ||||
|                                 log.debug("\tPermission test on node " + nodeService.getPath(testNodeRef)); | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 log.debug("\tPermission test on non-existing node " + testNodeRef); | ||||
|                             } | ||||
|                             return accessAbstainOrDeny; | ||||
|                         } | ||||
|                         if((hasMethodEntry == null) || (hasMethodEntry.booleanValue())) | ||||
|                         { | ||||
|                             return AccessDecisionVoter.ACCESS_GRANTED; | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             return AccessDecisionVoter.ACCESS_DENIED; | ||||
|                         } | ||||
|  | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     throw new ACLEntryVoterException("The specified parameter is not a NodeRef or ChildAssociationRef"); | ||||
|                     Object testObject = getArgument(invocation, cad.parameter[0]); | ||||
|                     //If the execution reaches here, then testNodeRef is always null | ||||
|                     testNodeRef = getNodeRef(testObject, nodeService); | ||||
|                 } | ||||
|             } | ||||
|             else if (cad.typeString.equals(ACL_ITEM)) | ||||
| @@ -584,44 +578,10 @@ public class ACLEntryVoter implements AccessDecisionVoter, InitializingBean | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (testNodeRef != null) | ||||
|             Integer accessAbstainOrDeny = shouldAbstainOrDeny(cad.required, testNodeRef, abstainForClassQNames, nodeService, permissionService); | ||||
|             if (accessAbstainOrDeny != null) | ||||
|             { | ||||
|                 // now we know the node - we can abstain for certain types and aspects (eg. RM) | ||||
|                 if(abstainForClassQNames.size() > 0) | ||||
|                 { | ||||
|                     // check node exists | ||||
|                     if (nodeService.exists(testNodeRef)) | ||||
|                     { | ||||
|                         QName typeQName = nodeService.getType(testNodeRef); | ||||
|                         if(abstainForClassQNames.contains(typeQName)) | ||||
|                         { | ||||
|                             return AccessDecisionVoter.ACCESS_ABSTAIN; | ||||
|                         } | ||||
|                         | ||||
|                         Set<QName> aspectQNames = nodeService.getAspects(testNodeRef); | ||||
|                         for(QName abstain : abstainForClassQNames) | ||||
|                         { | ||||
|                             if(aspectQNames.contains(abstain)) | ||||
|                             { | ||||
|                                 return AccessDecisionVoter.ACCESS_ABSTAIN; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                  | ||||
|                 if (log.isDebugEnabled()) | ||||
|                 { | ||||
|                     log.debug("\t\tNode ref is not null"); | ||||
|                 } | ||||
|                 if (permissionService.hasPermission(testNodeRef, cad.required.toString()) == AccessStatus.DENIED) | ||||
|                 { | ||||
|                     if (log.isDebugEnabled()) | ||||
|                     { | ||||
|                         log.debug("\t\tPermission is denied"); | ||||
|                         Thread.dumpStack(); | ||||
|                     } | ||||
|                     return AccessDecisionVoter.ACCESS_DENIED; | ||||
|                 } | ||||
|                 return accessAbstainOrDeny; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,175 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Repository | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2021 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is | ||||
|  * provided under the following open source license terms: | ||||
|  * | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.repo.security.permissions.impl.acegi; | ||||
|  | ||||
| import java.util.Set; | ||||
|  | ||||
| import net.sf.acegisecurity.vote.AccessDecisionVoter; | ||||
| import org.alfresco.repo.security.permissions.impl.SimplePermissionReference; | ||||
| 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.AccessStatus; | ||||
| import org.alfresco.service.cmr.security.PermissionService; | ||||
| import org.alfresco.service.namespace.QName; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Utility methods extracted from AclEntryVoter | ||||
|  * | ||||
|  * @author Lev Belava | ||||
|  */ | ||||
| final class ACLEntryVoterUtils | ||||
| { | ||||
|     private static final Logger LOG = LoggerFactory.getLogger(ACLEntryVoterUtils.class); | ||||
|  | ||||
|     private ACLEntryVoterUtils() | ||||
|     { | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Gets NodeRef for testObject based on inferred type | ||||
|      * | ||||
|      * @param testObject                Tested object to work on | ||||
|      * @param nodeService               Node service to perform checks on refs | ||||
|      * @return                          NodeRef for testObject or null if (testObject is null or StoreRef from testObject does not exist in the provided NodeService) | ||||
|      * @throws ACLEntryVoterException   if testObject is not null and not one of a NodeRef or ChildAssociationRef types | ||||
|      */ | ||||
|     static NodeRef getNodeRef(Object testObject, NodeService nodeService) | ||||
|     { | ||||
|         if (testObject == null) | ||||
|         { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         if (StoreRef.class.isAssignableFrom(testObject.getClass())) | ||||
|         { | ||||
|             LOG.debug("Permission test against the store - using permissions on the root node"); | ||||
|             StoreRef storeRef = (StoreRef) testObject; | ||||
|             if (nodeService.exists(storeRef)) | ||||
|             { | ||||
|                 return nodeService.getRootNode(storeRef); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 LOG.debug("StoreRef does not exist"); | ||||
|                 return null; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (NodeRef.class.isAssignableFrom(testObject.getClass())) | ||||
|         { | ||||
|             NodeRef result = (NodeRef) testObject; | ||||
|             if (LOG.isDebugEnabled()) | ||||
|             { | ||||
|                 if (nodeService.exists(result)) | ||||
|                 { | ||||
|                     LOG.debug("Permission test on node {}", nodeService.getPath(result)); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     LOG.debug("Permission test on non-existing node {}", result); | ||||
|                 } | ||||
|             } | ||||
|             return result; | ||||
|         } | ||||
|  | ||||
|         if (ChildAssociationRef.class.isAssignableFrom(testObject.getClass())) | ||||
|         { | ||||
|             ChildAssociationRef testChildRef = (ChildAssociationRef) testObject; | ||||
|             NodeRef result = testChildRef.getChildRef(); | ||||
|             if (LOG.isDebugEnabled()) | ||||
|             { | ||||
|                 if (nodeService.exists(result)) | ||||
|                 { | ||||
|                     LOG.debug("Permission test on node {}", nodeService.getPath(result)); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     LOG.debug("Permission test on non-existing node {}", result); | ||||
|                 } | ||||
|             } | ||||
|             return result; | ||||
|         } | ||||
|  | ||||
|         throw new ACLEntryVoterException("The specified parameter is not a NodeRef or ChildAssociationRef"); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Checks if tested NodeRef instance is abstained or denied based on set of QNames to abstain and | ||||
|      * | ||||
|      * @param requiredPermissionReference           Required permissions | ||||
|      * @param testNodeRef                           NodeRef to be verified | ||||
|      * @param abstainForClassQNames                 Set of QNames to abstain | ||||
|      * @param nodeService                           Node service to perform checks on tested NodeRef | ||||
|      * @param permissionService                     Permission service to check for required permissions | ||||
|      * @return                                      null if testNodeRef is not abstained or denied, otherwise returns appropriate status. | ||||
|      */ | ||||
|     static Integer shouldAbstainOrDeny(SimplePermissionReference requiredPermissionReference, NodeRef testNodeRef, Set<QName> abstainForClassQNames, | ||||
|                                         NodeService nodeService, PermissionService permissionService) | ||||
|     { | ||||
|         if (testNodeRef == null) | ||||
|         { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         LOG.debug("Node ref is not null"); | ||||
|  | ||||
|         if (abstainForClassQNames.size() > 0 && nodeService.exists(testNodeRef)) | ||||
|         { | ||||
|             if (abstainForClassQNames.contains(nodeService.getType(testNodeRef))) | ||||
|             { | ||||
|                 return AccessDecisionVoter.ACCESS_ABSTAIN; | ||||
|             } | ||||
|             Set<QName> testNodeRefAspects = nodeService.getAspects(testNodeRef); | ||||
|             for (QName abstain : abstainForClassQNames) | ||||
|             { | ||||
|                 if (testNodeRefAspects.contains(abstain)) | ||||
|                 { | ||||
|                     return AccessDecisionVoter.ACCESS_ABSTAIN; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (AccessStatus.DENIED == permissionService.hasPermission(testNodeRef, requiredPermissionReference.toString())) | ||||
|         { | ||||
|             if (LOG.isDebugEnabled()) | ||||
|             { | ||||
|                 LOG.debug("Permission is denied"); | ||||
|                 Thread.dumpStack(); | ||||
|             } | ||||
|             return AccessDecisionVoter.ACCESS_DENIED; | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -2,7 +2,7 @@ | ||||
|  * #%L | ||||
|  * Alfresco Repository | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2017 Alfresco Software Limited | ||||
|  * Copyright (C) 2005 - 2021 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software.  | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of  | ||||
| @@ -640,7 +640,7 @@ public class ThumbnailServiceImpl implements ThumbnailService, | ||||
|         boolean valid = true; | ||||
|         ContentData content = (ContentData) this.nodeService.getProperty(thumbnailNode, ContentModel.PROP_CONTENT); | ||||
|         // (MNT-17162) A thumbnail with an empty content is cached for post-transaction removal, to prevent the delete in read-only transactions.  | ||||
|         if (content.getSize() == 0) | ||||
|         if (content == null || content.getSize() == 0) | ||||
|         { | ||||
|             TransactionalResourceHelper.getSet(THUMBNAIL_TO_DELETE_NODES).add(thumbnailNode); | ||||
|             TransactionSupportUtil.bindListener(this.thumbnailsToDeleteTransactionListener, 0); | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
| repository.name=Main Repository | ||||
|  | ||||
| # Schema number | ||||
| version.schema=15002 | ||||
| version.schema=15100 | ||||
|  | ||||
| # Directory configuration | ||||
|  | ||||
|   | ||||
| @@ -209,6 +209,7 @@ import org.junit.runners.Suite; | ||||
|     org.alfresco.repo.security.authentication.AuthorizationTest.class, | ||||
|     org.alfresco.repo.security.permissions.PermissionCheckedCollectionTest.class, | ||||
|     org.alfresco.repo.security.permissions.impl.acegi.FilteringResultSetTest.class, | ||||
|     org.alfresco.repo.security.permissions.impl.acegi.ACLEntryVoterUtilsTest.class, | ||||
|     org.alfresco.repo.security.authentication.ChainingAuthenticationServiceTest.class, | ||||
|     org.alfresco.repo.security.authentication.NameBasedUserNameGeneratorTest.class, | ||||
|     org.alfresco.repo.version.common.VersionImplTest.class, | ||||
|   | ||||
| @@ -31,17 +31,18 @@ import java.io.PrintWriter; | ||||
| import java.io.Serializable; | ||||
| import java.io.StringWriter; | ||||
| import java.io.UnsupportedEncodingException; | ||||
| import java.util.Collections; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| import java.util.Locale; | ||||
| import java.util.Map; | ||||
|  | ||||
| import junit.framework.TestCase; | ||||
|  | ||||
| import org.alfresco.error.AlfrescoRuntimeException; | ||||
| import org.alfresco.model.ContentModel; | ||||
| import org.alfresco.repo.content.MimetypeMap; | ||||
| import org.alfresco.repo.dictionary.DictionaryDAO; | ||||
| import org.alfresco.repo.dictionary.NamespaceDAO; | ||||
| import org.alfresco.repo.i18n.MessageService; | ||||
| import org.alfresco.repo.node.db.DbNodeServiceImpl; | ||||
| import org.alfresco.repo.policy.BehaviourFilter; | ||||
| import org.alfresco.repo.security.authentication.AuthenticationUtil; | ||||
| @@ -50,6 +51,7 @@ import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransacti | ||||
| import org.alfresco.service.cmr.admin.RepoAdminService; | ||||
| 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.ContentService; | ||||
| import org.alfresco.service.cmr.repository.ContentWriter; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| @@ -67,6 +69,8 @@ import org.apache.commons.logging.LogFactory; | ||||
| import org.junit.experimental.categories.Category; | ||||
| import org.springframework.context.ApplicationContext; | ||||
|  | ||||
| import junit.framework.TestCase; | ||||
|  | ||||
| /** | ||||
|  * @see RepoAdminServiceImpl | ||||
|  *  | ||||
| @@ -88,6 +92,7 @@ public class RepoAdminServiceImplTest extends TestCase | ||||
|     private NamespaceService namespaceService; | ||||
|     private BehaviourFilter behaviourFilter; | ||||
|     private DictionaryDAO dictionaryDAO; | ||||
|     private MessageService messageService; | ||||
|      | ||||
|     final String modelPrefix = "model-"; | ||||
|     final static String MKR = "{MKR}"; | ||||
| @@ -140,6 +145,7 @@ public class RepoAdminServiceImplTest extends TestCase | ||||
|         namespaceService = (NamespaceService) ctx.getBean("NamespaceService"); | ||||
|         behaviourFilter = (BehaviourFilter)ctx.getBean("policyBehaviourFilter"); | ||||
|         dictionaryDAO = (DictionaryDAO) ctx.getBean("dictionaryDAO"); | ||||
|         messageService = (MessageService) ctx.getBean("MessageService"); | ||||
|          | ||||
|         DbNodeServiceImpl dbNodeService = (DbNodeServiceImpl)ctx.getBean("dbNodeService"); | ||||
|         dbNodeService.setEnableTimestampPropagation(false); | ||||
| @@ -865,8 +871,182 @@ public class RepoAdminServiceImplTest extends TestCase | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     // | ||||
|     // TODO - Test custom message management | ||||
|     // | ||||
|  | ||||
|     // Test deploy bundle from classpath | ||||
|     public void testDeployMessageBundleFromClasspath() throws Exception | ||||
|     { | ||||
|         String bundleBaseName = "mycustommessages"; | ||||
|         String resourceClasspath = "alfresco/extension/messages/" + bundleBaseName; | ||||
|  | ||||
|         final String message_key = "mycustommessages.key1"; | ||||
|         final String message_value_default = "This is a custom message"; | ||||
|         final String message_value_fr = "Ceci est un message personnalis\\u00e9"; | ||||
|         final String message_value_de = "Dies ist eine benutzerdefinierte Nachricht"; | ||||
|  | ||||
|         // Undeploy the bundle | ||||
|         if (repoAdminService.getMessageBundles().contains(bundleBaseName)) | ||||
|         { | ||||
|             repoAdminService.undeployMessageBundle(bundleBaseName); | ||||
|         } | ||||
|  | ||||
|         // Verify the custom bundle is registered | ||||
|         assertFalse("The custom bundle should not be deployed", repoAdminService.getMessageBundles().contains(bundleBaseName)); | ||||
|  | ||||
|         // Depoly the message bundle | ||||
|         repoAdminService.deployMessageBundle(resourceClasspath); | ||||
|  | ||||
|         // Reload the messages | ||||
|         repoAdminService.reloadMessageBundle(bundleBaseName); | ||||
|  | ||||
|         // Verify the custom bundle is registered | ||||
|         assertTrue("The custom bundle should be deployed", repoAdminService.getMessageBundles().contains(bundleBaseName)); | ||||
|  | ||||
|         // Verify we have the messages for each locale | ||||
|         AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); | ||||
|  | ||||
|         assertMessageValue("Cannot retrieve default message value", message_value_default, message_key, Locale.getDefault()); | ||||
|         assertMessageValue("Cannot retrieve french message value", message_value_fr, message_key, Locale.FRANCE); | ||||
|         assertMessageValue("Cannot retrieve german message value", message_value_de, message_key, Locale.GERMANY); | ||||
|  | ||||
|         // Test deploy a non existent bundle | ||||
|         try | ||||
|         { | ||||
|             repoAdminService.deployMessageBundle("alfresco/extension/messages/inexistentbundle"); | ||||
|             fail("Bundle was not supposed to be deployed"); | ||||
|         } | ||||
|         catch (Exception e) | ||||
|         { | ||||
|             // Expected to fail | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     // Test deploy bundle from repo and reload bundles | ||||
|     public void testDeployMessageBundleFromRepo() throws Exception | ||||
|     { | ||||
|         final String bundleBaseName = "repoBundle"; | ||||
|         final String message_key = "repoBundle.key1"; | ||||
|         final String message_value = "Value 1"; | ||||
|         final String message_value_fr = "Value FR"; | ||||
|         final String message_value_de = "Value DE"; | ||||
|         final String message_value_new = "New Value 1"; | ||||
|  | ||||
|         // Set location | ||||
|         NodeRef rootNodeRef = nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); | ||||
|         List<NodeRef> nodeRefs = searchService.selectNodes(rootNodeRef, "/app:company_home/app:dictionary/app:messages", null, | ||||
|                 namespaceService, false); | ||||
|         assertEquals(1, nodeRefs.size()); | ||||
|         NodeRef messagesNodeRef = nodeRefs.get(0); | ||||
|  | ||||
|         // Clear messages of this bundle if they exist and are registered | ||||
|         clearRepoBundles(messagesNodeRef); | ||||
|  | ||||
|         assertEquals(0, repoAdminService.getMessageBundles().size()); | ||||
|  | ||||
|         // Create and upload the message files | ||||
|         NodeRef messageNode_default = createMessagesNodeWithSingleKey(messagesNodeRef, bundleBaseName, message_key, null, | ||||
|                 message_value); | ||||
|         createMessagesNodeWithSingleKey(messagesNodeRef, bundleBaseName, message_key, Locale.FRANCE.toString(), message_value_fr); | ||||
|         createMessagesNodeWithSingleKey(messagesNodeRef, bundleBaseName, message_key, Locale.GERMANY.toString(), | ||||
|                 message_value_de); | ||||
|  | ||||
|         // Reload the messages | ||||
|         repoAdminService.reloadMessageBundle(bundleBaseName); | ||||
|  | ||||
|         // Verify we have the messages for each locale | ||||
|         AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); | ||||
|  | ||||
|         assertMessageValue("Cannot retrieve default message value", message_value, message_key, Locale.getDefault()); | ||||
|         assertMessageValue("Cannot retrieve french message value", message_value_fr, message_key, Locale.FRANCE); | ||||
|         assertMessageValue("Cannot retrieve german message value", message_value_de, message_key, Locale.GERMANY); | ||||
|  | ||||
|         // Change the values | ||||
|         putContentInMessageNode(messageNode_default, message_key, message_value_new); | ||||
|  | ||||
|         // Verify we still have the old value | ||||
|         assertMessageValue("Unexpected change of message value", message_value, message_key, Locale.getDefault()); | ||||
|  | ||||
|         // Reload the messages | ||||
|         repoAdminService.reloadMessageBundle(bundleBaseName); | ||||
|  | ||||
|         // Verify new values | ||||
|         assertMessageValue("Change of message value not reflected", message_value_new, message_key, Locale.getDefault()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create messages node | ||||
|      */ | ||||
|     private NodeRef createMessagesNodeWithSingleKey(NodeRef parentNode, String bundleName, String key, String locale, | ||||
|             String localeValue) | ||||
|     { | ||||
|         String msg_extension = ".properties"; | ||||
|         String filename = bundleName + msg_extension; | ||||
|         String messageValue = localeValue; | ||||
|  | ||||
|         if (locale != null) | ||||
|         { | ||||
|             filename = bundleName + "_" + locale + msg_extension; | ||||
|         } | ||||
|         // Create a model node | ||||
|         NodeRef messageNode = this.nodeService.createNode( | ||||
|                 parentNode,  | ||||
|                 ContentModel.ASSOC_CONTAINS, | ||||
|                 QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, filename),  | ||||
|                 ContentModel.TYPE_CONTENT, | ||||
|                 Collections.<QName, Serializable> singletonMap(ContentModel.PROP_NAME, filename) | ||||
|                 ).getChildRef(); | ||||
|  | ||||
|         putContentInMessageNode(messageNode, key, messageValue); | ||||
|  | ||||
|         return messageNode; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Write content of message node | ||||
|      */ | ||||
|     private void putContentInMessageNode(NodeRef nodeRef, String key, String value) | ||||
|     { | ||||
|         ContentWriter contentWriter = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true); | ||||
|         contentWriter.setEncoding("UTF-8"); | ||||
|         contentWriter.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); | ||||
|         String messagesString = key + "=" + value; | ||||
|         contentWriter.putContent(messagesString); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Clear Repo Bundle | ||||
|      */ | ||||
|     private void clearRepoBundles(NodeRef parentNode) | ||||
|     { | ||||
|         List<String> repoBundles = repoAdminService.getMessageBundles(); | ||||
|         for (String repoBundle : repoBundles) | ||||
|         { | ||||
|             repoAdminService.undeployMessageBundle(repoBundle); | ||||
|         } | ||||
|  | ||||
|         List<ChildAssociationRef> messageNodes = nodeService.getChildAssocs(parentNode); | ||||
|         for (ChildAssociationRef messageChildRef : messageNodes) | ||||
|         { | ||||
|             NodeRef messageNode = messageChildRef.getChildRef(); | ||||
|             nodeService.deleteNode(messageNode); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Clear Repo Bundle | ||||
|      */ | ||||
|     private void assertMessageValue(String errorMessage, String expectedValue, String key, Locale locale) | ||||
|     { | ||||
|         transactionService.getRetryingTransactionHelper() | ||||
|                 .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>() | ||||
|                 { | ||||
|                     public Void execute() throws Throwable | ||||
|                     { | ||||
|                         assertEquals(errorMessage, expectedValue, messageService.getMessage(key, locale)); | ||||
|                         return null; | ||||
|                     } | ||||
|                 }); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -25,9 +25,14 @@ | ||||
|  */ | ||||
| package org.alfresco.repo.dictionary; | ||||
|  | ||||
| import java.io.Serializable; | ||||
| import java.text.MessageFormat; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| import java.util.Locale; | ||||
|  | ||||
| import javax.transaction.UserTransaction; | ||||
|  | ||||
| import org.alfresco.model.ContentModel; | ||||
| import org.alfresco.repo.content.MimetypeMap; | ||||
| @@ -53,8 +58,6 @@ import org.junit.After; | ||||
| import org.junit.Before; | ||||
| import org.junit.Test; | ||||
| import org.junit.experimental.categories.Category; | ||||
|  | ||||
| import javax.transaction.UserTransaction; | ||||
| import org.springframework.transaction.annotation.Transactional; | ||||
|  | ||||
| @Category(BaseSpringTestsCategory.class) | ||||
| @@ -94,6 +97,14 @@ public class DictionaryRepositoryBootstrapTest extends BaseSpringTest | ||||
|          | ||||
|         "</model>"; | ||||
|      | ||||
|     public static final String MESSAGES_KEY = "my_bootstrap_test"; | ||||
|     public static final String MESSAGES_VALUE = "My Message"; | ||||
|     public static final String MESSAGES_VALUE_FR = "Mon message"; | ||||
|     public static final String FOLDERNAME_MODELS = "models"; | ||||
|     public static final String FOLDERNAME_MESSAGES = "messages"; | ||||
|     public static final String BUNDLENAME_MESSAGES = "testBootstap"; | ||||
|     public static final String FILENAME_MESSAGES_EXT = ".properties"; | ||||
|  | ||||
|     /** Behaviour filter */ | ||||
|     private BehaviourFilter behaviourFilter; | ||||
|  | ||||
| @@ -123,7 +134,8 @@ public class DictionaryRepositoryBootstrapTest extends BaseSpringTest | ||||
|  | ||||
|     private UserTransaction txn; | ||||
|     private StoreRef storeRef; | ||||
|     private NodeRef rootNodeRef; | ||||
|     private NodeRef rootModelsNodeRef; | ||||
|     private NodeRef rootMessagesNodeRef; | ||||
|  | ||||
|     @Before | ||||
|     public void before() throws Exception | ||||
| @@ -151,7 +163,17 @@ public class DictionaryRepositoryBootstrapTest extends BaseSpringTest | ||||
|  | ||||
|         // Create the store and get the root node | ||||
|         this.storeRef = this.nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis()); | ||||
|         this.rootNodeRef = this.nodeService.getRootNode(this.storeRef); | ||||
|  | ||||
|         NodeRef rootNodeRef = this.nodeService.getRootNode(this.storeRef); | ||||
|         this.rootModelsNodeRef = this.nodeService | ||||
|                 .createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, | ||||
|                         QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, FOLDERNAME_MODELS), ContentModel.TYPE_FOLDER) | ||||
|                 .getChildRef(); | ||||
|  | ||||
|         this.rootMessagesNodeRef = this.nodeService | ||||
|                 .createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, | ||||
|                         QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, FOLDERNAME_MESSAGES), ContentModel.TYPE_FOLDER) | ||||
|                 .getChildRef(); | ||||
|  | ||||
|         txn.commit(); | ||||
|  | ||||
| @@ -167,18 +189,20 @@ public class DictionaryRepositoryBootstrapTest extends BaseSpringTest | ||||
|         this.bootstrap.setNamespaceService(this.namespaceService); | ||||
|         this.bootstrap.setMessageService(this.messageService); | ||||
|         this.bootstrap.setPolicyComponent(this.policyComponent); | ||||
|          | ||||
|         RepositoryLocation location = new RepositoryLocation(); | ||||
|         location.setStoreProtocol(this.storeRef.getProtocol()); | ||||
|         location.setStoreId(this.storeRef.getIdentifier()); | ||||
|         location.setQueryLanguage(RepositoryLocation.LANGUAGE_PATH); | ||||
|         // NOTE: we are not setting the path for now .. in doing so we are searching the root node only | ||||
|          | ||||
|         List<RepositoryLocation> locations = new ArrayList<RepositoryLocation>(); | ||||
|         locations.add(location); | ||||
|          | ||||
|         this.bootstrap.setRepositoryModelsLocations(locations); | ||||
|          | ||||
|  | ||||
|         RepositoryLocation modelsLocation = new RepositoryLocation(this.storeRef, | ||||
|                 this.nodeService.getPath(rootModelsNodeRef).toPrefixString(namespaceService), RepositoryLocation.LANGUAGE_PATH); | ||||
|         RepositoryLocation messagesLocation = new RepositoryLocation(this.storeRef, | ||||
|                 this.nodeService.getPath(rootMessagesNodeRef).toPrefixString(namespaceService), RepositoryLocation.LANGUAGE_PATH); | ||||
|  | ||||
|         List<RepositoryLocation> modelsLocations = new ArrayList<RepositoryLocation>(); | ||||
|         modelsLocations.add(modelsLocation); | ||||
|         List<RepositoryLocation> messagesLocations = new ArrayList<RepositoryLocation>(); | ||||
|         messagesLocations.add(messagesLocation); | ||||
|  | ||||
|         this.bootstrap.setRepositoryModelsLocations(modelsLocations); | ||||
|         this.bootstrap.setRepositoryMessagesLocations(messagesLocations); | ||||
|  | ||||
|         // register with dictionary service | ||||
|         this.bootstrap.register(); | ||||
|         txn.commit(); | ||||
| @@ -224,7 +248,15 @@ public class DictionaryRepositoryBootstrapTest extends BaseSpringTest | ||||
|                 "Test model one", | ||||
|                 "base1", | ||||
|                 "prop1"); | ||||
|                 | ||||
|  | ||||
|         // Create a message file for the default locale | ||||
|         NodeRef messageNodeDefaultLoc = createMessagesNode(null, null); | ||||
|         // Create a message file for the french locale | ||||
|         createMessagesNode(Locale.FRANCE.toString(), MESSAGES_VALUE_FR); | ||||
|         // Construct baseBundleName for validation | ||||
|         String baseBundleName = storeRef.toString() | ||||
|                 + messageService.getBaseBundleName(nodeService.getPath(messageNodeDefaultLoc).toPrefixString(namespaceService)); | ||||
|  | ||||
|         // Check that the model is not in the dictionary yet | ||||
|         try | ||||
|         { | ||||
| @@ -251,6 +283,12 @@ public class DictionaryRepositoryBootstrapTest extends BaseSpringTest | ||||
|                 QName.createQName("http://www.alfresco.org/model/test3DictionaryBootstrapFromRepo/1.0", "testModel3")); | ||||
|         assertNotNull(modelDefinition3); | ||||
|  | ||||
|         // Check if the messages were registered correctly | ||||
|         assertTrue("The message bundle should be registered", messageService.getRegisteredBundles().contains(baseBundleName)); | ||||
|         assertEquals("The default message value is not as expected", MESSAGES_VALUE, messageService.getMessage(MESSAGES_KEY)); | ||||
|         assertEquals("The message value in french is not as expected", MESSAGES_VALUE_FR, | ||||
|                 messageService.getMessage(MESSAGES_KEY, Locale.FRANCE)); | ||||
|  | ||||
|         txn.commit(); | ||||
|     } | ||||
|  | ||||
| @@ -277,7 +315,7 @@ public class DictionaryRepositoryBootstrapTest extends BaseSpringTest | ||||
|     { | ||||
|         // Create a model node | ||||
|         NodeRef model = this.nodeService.createNode( | ||||
|                 this.rootNodeRef, | ||||
|                 this.rootModelsNodeRef, | ||||
|                 ContentModel.ASSOC_CHILDREN, | ||||
|                 QName.createQName("{test}models"), | ||||
|                 ContentModel.TYPE_DICTIONARY_MODEL).getChildRef(); | ||||
| @@ -300,6 +338,39 @@ public class DictionaryRepositoryBootstrapTest extends BaseSpringTest | ||||
|         return model; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Create messages node | ||||
|      *  | ||||
|      * @return NodeRef | ||||
|      */ | ||||
|     private NodeRef createMessagesNode(String locale, String localeValue) | ||||
|     { | ||||
|         String filename = BUNDLENAME_MESSAGES + FILENAME_MESSAGES_EXT; | ||||
|         String messageValue = MESSAGES_VALUE; | ||||
|  | ||||
|         if (locale != null) | ||||
|         { | ||||
|             filename = BUNDLENAME_MESSAGES + "_" + locale + FILENAME_MESSAGES_EXT; | ||||
|             messageValue = localeValue; | ||||
|         } | ||||
|         // Create a model node | ||||
|         NodeRef messageNode = this.nodeService.createNode( | ||||
|                 this.rootMessagesNodeRef,  | ||||
|                 ContentModel.ASSOC_CHILDREN, | ||||
|                 QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, filename),  | ||||
|                 ContentModel.TYPE_CONTENT, | ||||
|                 Collections.<QName, Serializable> singletonMap(ContentModel.PROP_NAME, filename) | ||||
|                 ).getChildRef(); | ||||
|  | ||||
|         ContentWriter contentWriter = this.contentService.getWriter(messageNode, ContentModel.PROP_CONTENT, true); | ||||
|         contentWriter.setEncoding("UTF-8"); | ||||
|         contentWriter.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); | ||||
|         String messagesString = MESSAGES_KEY + "=" + messageValue; | ||||
|         contentWriter.putContent(messagesString); | ||||
|  | ||||
|         return messageNode; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      *  | ||||
|      * Gets the model string  | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|  * #%L | ||||
|  * Alfresco Repository | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2018 Alfresco Software Limited | ||||
|  * Copyright (C) 2005 - 2021 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
| @@ -25,7 +25,16 @@ | ||||
|  */ | ||||
| package org.alfresco.repo.rendition2; | ||||
|  | ||||
| import junit.framework.AssertionFailedError; | ||||
| import static java.lang.Thread.sleep; | ||||
| import static org.alfresco.model.ContentModel.PROP_CONTENT; | ||||
| import static org.alfresco.model.RenditionModel.PROP_RENDITION_CONTENT_HASH_CODE; | ||||
| import static org.alfresco.repo.content.MimetypeMap.EXTENSION_BINARY; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.io.FileNotFoundException; | ||||
| import java.io.Serializable; | ||||
| import java.util.Collections; | ||||
|  | ||||
| import org.alfresco.model.ContentModel; | ||||
| import org.alfresco.repo.content.MimetypeMap; | ||||
| import org.alfresco.repo.content.metadata.AsynchronousExtractor; | ||||
| @@ -60,15 +69,7 @@ import org.quartz.CronExpression; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.util.ResourceUtils; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.io.FileNotFoundException; | ||||
| import java.io.Serializable; | ||||
| import java.util.Collections; | ||||
| import java.util.Map; | ||||
|  | ||||
| import static java.lang.Thread.sleep; | ||||
| import static org.alfresco.model.ContentModel.PROP_CONTENT; | ||||
| import static org.alfresco.repo.content.MimetypeMap.EXTENSION_BINARY; | ||||
| import junit.framework.AssertionFailedError; | ||||
|  | ||||
| /** | ||||
|  * Class unites common utility methods for {@link org.alfresco.repo.rendition2} package tests. | ||||
| @@ -531,4 +532,41 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest | ||||
|         RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); | ||||
|         txnHelper.doInTransaction(createUserCallback); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Helper method to check if the supplied content hash code is valid or not | ||||
|      * | ||||
|      * @param contentHashCode | ||||
|      *            the hash code to verify | ||||
|      * | ||||
|      * @return true in case it is an actual hash code, false otherwise | ||||
|      */ | ||||
|     protected boolean isValidRenditionContentHashCode(int contentHashCode) | ||||
|     { | ||||
|         return contentHashCode != RenditionService2Impl.RENDITION2_DOES_NOT_EXIST | ||||
|                 && contentHashCode != RenditionService2Impl.SOURCE_HAS_NO_CONTENT; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Helper method which gets the content hash code from the supplied rendition node without specific validations (the | ||||
|      * equivalent method from {@link RenditionService2Impl} is not exposed) | ||||
|      * | ||||
|      * @param renditionNodeRef | ||||
|      *            the rendition node | ||||
|      * | ||||
|      * @return -1 in case of there is no content, -2 in case rendition doesn't exist, the actual content hash code | ||||
|      *         otherwise | ||||
|      */ | ||||
|     protected int getRenditionContentHashCode(NodeRef renditionNodeRef) | ||||
|     { | ||||
|         int renditionContentHashCode = RenditionService2Impl.RENDITION2_DOES_NOT_EXIST; | ||||
|  | ||||
|         if (renditionNodeRef != null) | ||||
|         { | ||||
|             Serializable hashCode = nodeService.getProperty(renditionNodeRef, PROP_RENDITION_CONTENT_HASH_CODE); | ||||
|             renditionContentHashCode = hashCode == null ? RenditionService2Impl.SOURCE_HAS_NO_CONTENT : (int) hashCode; | ||||
|         } | ||||
|  | ||||
|         return renditionContentHashCode; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|  * #%L | ||||
|  * Alfresco Repository | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2018 Alfresco Software Limited | ||||
|  * Copyright (C) 2005 - 2021 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
| @@ -25,6 +25,11 @@ | ||||
|  */ | ||||
| package org.alfresco.repo.rendition2; | ||||
|  | ||||
| import static org.alfresco.model.ContentModel.PROP_CONTENT; | ||||
| import static org.junit.Assert.assertNotEquals; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| import org.alfresco.model.ContentModel; | ||||
| import org.alfresco.model.RenditionModel; | ||||
| import org.alfresco.repo.security.authentication.AuthenticationUtil; | ||||
| @@ -36,16 +41,9 @@ import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; | ||||
| import org.alfresco.service.cmr.security.PermissionService; | ||||
| import org.alfresco.service.namespace.NamespaceService; | ||||
| import org.alfresco.service.namespace.QName; | ||||
| import org.junit.AfterClass; | ||||
| import org.junit.Before; | ||||
| import org.junit.BeforeClass; | ||||
| import org.junit.Test; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| import static org.alfresco.model.ContentModel.PROP_CONTENT; | ||||
| import static org.junit.Assert.assertNotEquals; | ||||
|  | ||||
| /** | ||||
|  * Integration tests for {@link RenditionService2} | ||||
|  */ | ||||
| @@ -308,6 +306,99 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati | ||||
|         assertTrue("The rendition should be generated by new Rendition Service", hasRenditionedAspect); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Tests new {@link RenditionService2Impl#clearRenditionContentData(NodeRef, String)} method. | ||||
|      * <p> | ||||
|      * This method cleans a rendition content and content hash code. | ||||
|      * </p> | ||||
|      */ | ||||
|     @Test | ||||
|     public void testClearRenditionContentData() | ||||
|     { | ||||
|         // Create a node | ||||
|         NodeRef sourceNodeRef = createSource(ADMIN, "quick.jpg"); | ||||
|  | ||||
|         // Trigger the rendition | ||||
|         render(ADMIN, sourceNodeRef, DOC_LIB); | ||||
|         NodeRef renditionNodeRef = waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true); | ||||
|         assertNotNull("Rendition was not generated", renditionNodeRef); | ||||
|         assertNotNull("Rendition was not generated", nodeService.getProperty(renditionNodeRef, ContentModel.PROP_CONTENT)); | ||||
|  | ||||
|         // Check the rendition content hash code is valid | ||||
|         int contentHashCode = getRenditionContentHashCode(renditionNodeRef); | ||||
|         assertTrue("Rendition content hash code was not generated", isValidRenditionContentHashCode(contentHashCode)); | ||||
|  | ||||
|         // Disable renditionService2 | ||||
|         renditionService2.setEnabled(false); | ||||
|  | ||||
|         // Call 'clearRenditionContentData' method directly to prove rendition content will be cleaned | ||||
|         AuthenticationUtil.runAs((AuthenticationUtil.RunAsWork<Void>) () -> | ||||
|             transactionService.getRetryingTransactionHelper().doInTransaction(() -> | ||||
|             { | ||||
|                 renditionService2.clearRenditionContentData(sourceNodeRef, DOC_LIB); | ||||
|                 return null; | ||||
|             }), ADMIN); | ||||
|  | ||||
|         // The rendition should not have content by now | ||||
|         assertNull("Rendition has content", nodeService.getProperty(renditionNodeRef, ContentModel.PROP_CONTENT)); | ||||
|  | ||||
|         // The rendition should not have a content hash code by now | ||||
|         contentHashCode = getRenditionContentHashCode(renditionNodeRef); | ||||
|         assertFalse("Rendition has content hash code", isValidRenditionContentHashCode(contentHashCode)); | ||||
|  | ||||
|         // Enable renditionService2 | ||||
|         renditionService2.setEnabled(true); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Tests if a rendition without content (but with contentHashCode) can be generated again. | ||||
|      * <p> | ||||
|      * If the rendition consumption receives a null InputStream, the contentHashCode should be cleaned from the | ||||
|      * rendition node, allowing new requests to generate the rendition. | ||||
|      * </p> | ||||
|      */ | ||||
|     @Test | ||||
|     public void testRenditionWithoutContentButWithContentHashCode() | ||||
|     { | ||||
|         // Create a node with an actual rendition | ||||
|         NodeRef sourceNodeRef = createSource(ADMIN, "quick.jpg"); | ||||
|         render(ADMIN, sourceNodeRef, DOC_LIB); | ||||
|         NodeRef renditionNodeRef = waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true); | ||||
|         assertNotNull("Rendition was not generated", renditionNodeRef); | ||||
|         assertNotNull("Rendition was not generated", nodeService.getProperty(renditionNodeRef, ContentModel.PROP_CONTENT)); | ||||
|  | ||||
|         // Clear rendition content (at this point we're forcing a rendition without content but with hash code) | ||||
|         clearContent(ADMIN, renditionNodeRef); | ||||
|         assertNull("Rendition has content", nodeService.getProperty(renditionNodeRef, ContentModel.PROP_CONTENT)); | ||||
|  | ||||
|         // Check that rendition still has the content hash code | ||||
|         final int contentHashCode = getRenditionContentHashCode(renditionNodeRef); | ||||
|         assertTrue("Rendition content hash code was not generated", isValidRenditionContentHashCode(contentHashCode)); | ||||
|  | ||||
|         // Call the 'consume' method directly supplying a null InputStream | ||||
|         // This is explicitly called to prove that rendition hash code will be cleaned (as opposite to previous behavior) | ||||
|         RenditionDefinition2 renditionDefinition = renditionDefinitionRegistry2.getRenditionDefinition(DOC_LIB); | ||||
|         AuthenticationUtil.runAs(() -> { | ||||
|             renditionService2.consume(sourceNodeRef, null, renditionDefinition, contentHashCode); | ||||
|             return null; | ||||
|         }, ADMIN); | ||||
|         waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, false); | ||||
|  | ||||
|         // The content hash code should have been cleaned from the rendition node | ||||
|         int contentHashCode2 = getRenditionContentHashCode(renditionNodeRef); | ||||
|         assertFalse("Rendition content hash code was not cleaned", isValidRenditionContentHashCode(contentHashCode2)); | ||||
|  | ||||
|         // Attempts to render the rendition again | ||||
|         render(ADMIN, sourceNodeRef, DOC_LIB); | ||||
|         NodeRef renditionNodeRef2 = waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true); | ||||
|         assertEquals("New rendition nodeRef is different", renditionNodeRef.toString(), renditionNodeRef2.toString()); | ||||
|         assertNotNull("New rendition content was not generated", nodeService.getProperty(renditionNodeRef2, ContentModel.PROP_CONTENT)); | ||||
|  | ||||
|         // Check the new rendition content hash code | ||||
|         int contentHashCode3 = getRenditionContentHashCode(renditionNodeRef2); | ||||
|         assertTrue("New rendition content hash code was not generated", isValidRenditionContentHashCode(contentHashCode3)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @deprecated can be removed when we remove the original RenditionService | ||||
|      */ | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|  * #%L | ||||
|  * Alfresco Repository | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2016 Alfresco Software Limited | ||||
|  * Copyright (C) 2005 - 2021 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software.  | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of  | ||||
| @@ -96,6 +96,8 @@ public abstract class AbstractPermissionTest extends TestCase | ||||
|  | ||||
|     protected NodeRef systemNodeRef; | ||||
|  | ||||
|     protected NodeRef abstainedNode; | ||||
|  | ||||
|     protected AuthenticationComponent authenticationComponent; | ||||
|  | ||||
|     protected ModelDAO permissionModelDAO; | ||||
| @@ -186,6 +188,8 @@ public abstract class AbstractPermissionTest extends TestCase | ||||
|         props = createPersonProperties(USER2_LEMUR); | ||||
|         nodeService.createNode(typesNodeRef, children, ContentModel.TYPE_PERSON, container, props).getChildRef(); | ||||
|  | ||||
|         abstainedNode= nodeService.createNode(rootNodeRef, ContentModel.ASSOC_FAILED_THUMBNAIL, system, ContentModel.TYPE_FAILED_THUMBNAIL).getChildRef(); | ||||
|  | ||||
|         // create an authentication object e.g. the user | ||||
|         if(authenticationDAO.userExists(USER1_ANDY)) | ||||
|         { | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -0,0 +1,181 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Repository | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2021 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is | ||||
|  * provided under the following open source license terms: | ||||
|  * | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.repo.security.permissions.impl.acegi; | ||||
|  | ||||
| import static org.alfresco.repo.security.permissions.impl.acegi.ACLEntryVoterUtils.getNodeRef; | ||||
| import static org.alfresco.repo.security.permissions.impl.acegi.ACLEntryVoterUtils.shouldAbstainOrDeny; | ||||
| import static org.hamcrest.CoreMatchers.is; | ||||
| import static org.hamcrest.CoreMatchers.nullValue; | ||||
| import static org.hamcrest.MatcherAssert.assertThat; | ||||
| import static org.mockito.ArgumentMatchers.eq; | ||||
| import static org.mockito.ArgumentMatchers.nullable; | ||||
| import static org.mockito.Mockito.when; | ||||
|  | ||||
| import java.util.Collections; | ||||
| import java.util.Set; | ||||
|  | ||||
| import net.sf.acegisecurity.vote.AccessDecisionVoter; | ||||
| import org.alfresco.repo.security.permissions.impl.SimplePermissionReference; | ||||
| 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.AccessStatus; | ||||
| import org.alfresco.service.cmr.security.PermissionService; | ||||
| import org.alfresco.service.namespace.QName; | ||||
| import org.junit.Before; | ||||
| import org.junit.Test; | ||||
| import org.junit.runner.RunWith; | ||||
| import org.mockito.Mock; | ||||
| import org.mockito.junit.MockitoJUnitRunner; | ||||
|  | ||||
|  | ||||
| @RunWith(MockitoJUnitRunner.class) | ||||
| public class ACLEntryVoterUtilsTest | ||||
| { | ||||
|     private final NodeRef testNodeRef = new NodeRef("workspace://testNodeRef/testNodeRef"); | ||||
|     private final NodeRef rootNodeRef = new NodeRef("workspace://rootNodeRef/rootNodeRef"); | ||||
|     private final NodeRef refNodeForTestObject = new NodeRef("workspace://refNodeForTestObject/refNodeForTestObject"); | ||||
|     private final NodeRef childRefNode = new NodeRef("workspace://childRefNode/childRefNode"); | ||||
|     private final StoreRef testStoreNodeRef = new StoreRef("system://testStoreRefMock/testStoreRefMock"); | ||||
|     private final SimplePermissionReference simplePermissionReference = SimplePermissionReference.getPermissionReference(QName.createQName("uri", "local"), "Write"); | ||||
|     private final QName qNameToAbstain1 = QName.createQName("{test}testnode1"); | ||||
|     private final QName qNameToAbstain2 = QName.createQName("{test}testnode2"); | ||||
|     private final QName qNameToAbstain3 = QName.createQName("{test}testnode3"); | ||||
|     private final QName qNameNotFromTheAbstainSet = QName.createQName("{test}testnodeAbstain"); | ||||
|     private final Set<QName> qNamesToAbstain = Set.of(qNameToAbstain1, qNameToAbstain2, qNameToAbstain3); | ||||
|     @Mock | ||||
|     private PermissionService permissionServiceMock; | ||||
|     @Mock | ||||
|     private NodeService nodeServiceMock; | ||||
|     @Mock | ||||
|     private ChildAssociationRef childAssocRefMock; | ||||
|  | ||||
|     @Before | ||||
|     public void setUp() | ||||
|     { | ||||
|         when(nodeServiceMock.exists(testStoreNodeRef)).thenReturn(Boolean.TRUE); | ||||
|         when(nodeServiceMock.exists(testNodeRef)).thenReturn(Boolean.TRUE); | ||||
|         when(nodeServiceMock.getRootNode(testStoreNodeRef)).thenReturn(rootNodeRef); | ||||
|         when(nodeServiceMock.getType(testNodeRef)).thenReturn(qNameNotFromTheAbstainSet); | ||||
|         when(nodeServiceMock.getAspects(testNodeRef)).thenReturn(Set.of(qNameNotFromTheAbstainSet)); | ||||
|         when(permissionServiceMock.hasPermission(eq(testNodeRef), nullable(String.class))).thenReturn(AccessStatus.DENIED); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void returnsAccessDeniedFromPermissionService() | ||||
|     { | ||||
|         assertThat(shouldAbstainOrDeny(simplePermissionReference, testNodeRef, qNamesToAbstain, nodeServiceMock, permissionServiceMock), | ||||
|                    is(AccessDecisionVoter.ACCESS_DENIED)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void returnsNullOnNullTestObject() | ||||
|     { | ||||
|         assertThat(getNodeRef(null, nodeServiceMock), is(nullValue())); | ||||
|     } | ||||
|  | ||||
|     @Test(expected = ACLEntryVoterException.class) | ||||
|     public void throwsExceptionWhenParameterIsNotNodeRefOrChildAssociationRef() | ||||
|     { | ||||
|         getNodeRef("TEST", nodeServiceMock); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void returnsGivenTestNodeRefWhenStoreRefDoesNotExist() | ||||
|     { | ||||
|         when(nodeServiceMock.exists(testStoreNodeRef)).thenReturn(Boolean.FALSE); | ||||
|         assertThat(getNodeRef(testStoreNodeRef, nodeServiceMock), is(nullValue())); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void returnsRootNode() | ||||
|     { | ||||
|         assertThat(getNodeRef(testStoreNodeRef, nodeServiceMock), is(rootNodeRef)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void returnsNodeRefFromTestObject() | ||||
|     { | ||||
|         assertThat(getNodeRef(refNodeForTestObject, nodeServiceMock), is(refNodeForTestObject)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void returnsChildRefFromChildAssocRef() | ||||
|     { | ||||
|         when(childAssocRefMock.getChildRef()).thenReturn(childRefNode); | ||||
|         assertThat(getNodeRef(childAssocRefMock, nodeServiceMock), is(childRefNode)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void returnsNullOnNullTestNodeRef() | ||||
|     { | ||||
|         assertThat(shouldAbstainOrDeny(simplePermissionReference, null, qNamesToAbstain, nodeServiceMock, permissionServiceMock), | ||||
|                    is(nullValue())); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void returnsNullOnAbstainClassQnamesIsEmptyAndThereAreNoDeniedPermissions() | ||||
|     { | ||||
|         when(permissionServiceMock.hasPermission(eq(testNodeRef), nullable(String.class))).thenReturn(AccessStatus.ALLOWED); | ||||
|         assertThat(shouldAbstainOrDeny(simplePermissionReference, testNodeRef, Collections.emptySet(), nodeServiceMock, permissionServiceMock), | ||||
|                    is(nullValue())); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void returnsNullOnTestNodeRefDoesNotExistAndThereAreNoDeniedPermissions() | ||||
|     { | ||||
|         when(nodeServiceMock.exists(testNodeRef)).thenReturn(Boolean.FALSE); | ||||
|         when(permissionServiceMock.hasPermission(eq(testNodeRef), nullable(String.class))).thenReturn(AccessStatus.ALLOWED); | ||||
|         assertThat(shouldAbstainOrDeny(simplePermissionReference, testNodeRef, qNamesToAbstain, nodeServiceMock, permissionServiceMock), | ||||
|                    is(nullValue())); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void returnsNullOnNodeTypeAndNodeAspectsAreNotInSetToAbstainAndThereAreNoDeniedPermissions() | ||||
|     { | ||||
|         when(permissionServiceMock.hasPermission(eq(testNodeRef), nullable(String.class))).thenReturn(AccessStatus.ALLOWED); | ||||
|         assertThat(shouldAbstainOrDeny(simplePermissionReference, testNodeRef, qNamesToAbstain, nodeServiceMock, permissionServiceMock), | ||||
|                    is(nullValue())); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void returnsAbstainWhenNodeRefTypeIsInSetToAbstain() | ||||
|     { | ||||
|         when(nodeServiceMock.getType(testNodeRef)).thenReturn(qNameToAbstain2); | ||||
|         assertThat(shouldAbstainOrDeny(simplePermissionReference, testNodeRef, qNamesToAbstain, nodeServiceMock, permissionServiceMock), | ||||
|                    is(AccessDecisionVoter.ACCESS_ABSTAIN)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void returnsAbstainWhenAtLeastOneAspectIsInSetToAbstain() | ||||
|     { | ||||
|         when(nodeServiceMock.getAspects(testNodeRef)).thenReturn(Set.of(qNameNotFromTheAbstainSet, qNameToAbstain3)); | ||||
|         assertThat(shouldAbstainOrDeny(simplePermissionReference, testNodeRef, qNamesToAbstain, nodeServiceMock, permissionServiceMock), | ||||
|                    is(AccessDecisionVoter.ACCESS_ABSTAIN)); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1 @@ | ||||
| mycustommessages.key1=This is a custom message | ||||
| @@ -0,0 +1 @@ | ||||
| mycustommessages.key1=Dies ist eine benutzerdefinierte Nachricht | ||||
| @@ -0,0 +1 @@ | ||||
| mycustommessages.key1=Ceci est un message personnalis\u00e9 | ||||
		Reference in New Issue
	
	Block a user