mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
Merging from EC-MC: Checkpoint before refactor
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@5744 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -323,7 +323,7 @@
|
|||||||
<bean id="admLuceneIndexerAndSearcherFactory"
|
<bean id="admLuceneIndexerAndSearcherFactory"
|
||||||
class="org.alfresco.repo.search.impl.lucene.ADMLuceneIndexerAndSearcherFactory">
|
class="org.alfresco.repo.search.impl.lucene.ADMLuceneIndexerAndSearcherFactory">
|
||||||
<property name="nodeService">
|
<property name="nodeService">
|
||||||
<ref bean="nodeService" />
|
<ref bean="mlAwareNodeService" />
|
||||||
</property>
|
</property>
|
||||||
<property name="dictionaryService">
|
<property name="dictionaryService">
|
||||||
<ref bean="dictionaryService" />
|
<ref bean="dictionaryService" />
|
||||||
@@ -845,7 +845,7 @@
|
|||||||
<ref bean="policyComponent" />
|
<ref bean="policyComponent" />
|
||||||
</property>
|
</property>
|
||||||
<property name="nodeService">
|
<property name="nodeService">
|
||||||
<ref bean="nodeService" />
|
<ref bean="mlAwareNodeService" />
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
@@ -855,7 +855,7 @@
|
|||||||
<ref bean="policyComponent" />
|
<ref bean="policyComponent" />
|
||||||
</property>
|
</property>
|
||||||
<property name="nodeService">
|
<property name="nodeService">
|
||||||
<ref bean="nodeService" />
|
<ref bean="mlAwareNodeService" />
|
||||||
</property>
|
</property>
|
||||||
<property name="multilingualContentService">
|
<property name="multilingualContentService">
|
||||||
<ref bean="multilingualContentService" />
|
<ref bean="multilingualContentService" />
|
||||||
|
@@ -12,6 +12,7 @@
|
|||||||
<property name="copyService"><ref bean="copyService" /></property>
|
<property name="copyService"><ref bean="copyService" /></property>
|
||||||
<property name="searchService"><ref bean="searchService" /></property>
|
<property name="searchService"><ref bean="searchService" /></property>
|
||||||
<property name="contentService"><ref bean="contentService" /></property>
|
<property name="contentService"><ref bean="contentService" /></property>
|
||||||
|
<property name="multilingualContentService"><ref bean="multilingualContentService" /></property>
|
||||||
<property name="mimetypeService"><ref bean="mimetypeService" /></property>
|
<property name="mimetypeService"><ref bean="mimetypeService" /></property>
|
||||||
|
|
||||||
<property name="systemPaths">
|
<property name="systemPaths">
|
||||||
@@ -27,6 +28,16 @@
|
|||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<bean id="mlTranslationInterceptor" class="org.alfresco.repo.model.filefolder.MLTranslationInterceptor" >
|
||||||
|
<property name="nodeService">
|
||||||
|
<ref bean="nodeService"/>
|
||||||
|
</property>
|
||||||
|
<property name="multilingualContentService">
|
||||||
|
<ref bean="multilingualContentService"/>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
|
||||||
<bean name="tempFileMarkerInterceptor" class="org.alfresco.repo.model.filefolder.TempFileMarkerInterceptor">
|
<bean name="tempFileMarkerInterceptor" class="org.alfresco.repo.model.filefolder.TempFileMarkerInterceptor">
|
||||||
<property name="nodeService">
|
<property name="nodeService">
|
||||||
<ref bean="nodeService" />
|
<ref bean="nodeService" />
|
||||||
|
@@ -6,23 +6,33 @@
|
|||||||
|
|
||||||
<bean id="mlPropertyInterceptor" class="org.alfresco.repo.node.MLPropertyInterceptor">
|
<bean id="mlPropertyInterceptor" class="org.alfresco.repo.node.MLPropertyInterceptor">
|
||||||
<property name="directNodeService">
|
<property name="directNodeService">
|
||||||
<ref bean="nodeService"></ref>
|
<ref bean="mlAwareNodeService" />
|
||||||
|
</property>
|
||||||
|
<property name="directMultilingualContentService">
|
||||||
|
<ref bean="multilingualContentService" />
|
||||||
</property>
|
</property>
|
||||||
<property name="dictionaryService">
|
<property name="dictionaryService">
|
||||||
<ref bean="dictionaryService" />
|
<ref bean="dictionaryService" />
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="mlTranslationInterceptor" class="org.alfresco.repo.node.MLTranslationInterceptor">
|
<bean id="nodeService" class="org.springframework.aop.framework.ProxyFactoryBean" >
|
||||||
<property name="directNodeService">
|
<property name="targetName">
|
||||||
<ref bean="nodeService"/>
|
<value>mlAwareNodeService</value>
|
||||||
</property>
|
</property>
|
||||||
<property name="multilingualContentService">
|
<property name="proxyInterfaces">
|
||||||
<ref bean="multilingualContentService"/>
|
<list>
|
||||||
|
<value>org.alfresco.service.cmr.repository.NodeService</value>
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
<property name="interceptorNames">
|
||||||
|
<list>
|
||||||
|
<value>mlPropertyInterceptor</value>
|
||||||
|
</list>
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="nodeService" class="org.alfresco.repo.service.StoreRedirectorProxyFactory">
|
<bean id="mlAwareNodeService" class="org.alfresco.repo.service.StoreRedirectorProxyFactory">
|
||||||
<property name="proxyInterface">
|
<property name="proxyInterface">
|
||||||
<value>org.alfresco.service.cmr.repository.NodeService</value>
|
<value>org.alfresco.service.cmr.repository.NodeService</value>
|
||||||
</property>
|
</property>
|
||||||
|
@@ -69,8 +69,6 @@
|
|||||||
<idref local="AuditMethodInterceptor"/>
|
<idref local="AuditMethodInterceptor"/>
|
||||||
<idref local="exceptionTranslator"/>
|
<idref local="exceptionTranslator"/>
|
||||||
<idref bean="NodeService_security"/>
|
<idref bean="NodeService_security"/>
|
||||||
<idref bean="mlTranslationInterceptor"/>
|
|
||||||
<idref bean="mlPropertyInterceptor"/>
|
|
||||||
</list>
|
</list>
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
@@ -656,6 +654,7 @@
|
|||||||
<idref local="AuditMethodInterceptor"/>
|
<idref local="AuditMethodInterceptor"/>
|
||||||
<idref local="exceptionTranslator"/>
|
<idref local="exceptionTranslator"/>
|
||||||
<idref bean="FileFolderService_security"/>
|
<idref bean="FileFolderService_security"/>
|
||||||
|
<idref bean="mlTranslationInterceptor"/>
|
||||||
<idref bean="tempFileMarkerInterceptor"/>
|
<idref bean="tempFileMarkerInterceptor"/>
|
||||||
</list>
|
</list>
|
||||||
</property>
|
</property>
|
||||||
@@ -1166,6 +1165,8 @@
|
|||||||
</property>
|
</property>
|
||||||
<property name="transactionAttributes">
|
<property name="transactionAttributes">
|
||||||
<props>
|
<props>
|
||||||
|
<prop key="is*">${server.transaction.mode.readOnly}</prop>
|
||||||
|
<prop key="get*">${server.transaction.mode.readOnly}</prop>
|
||||||
<prop key="*">${server.transaction.mode.default}</prop>
|
<prop key="*">${server.transaction.mode.default}</prop>
|
||||||
</props>
|
</props>
|
||||||
</property>
|
</property>
|
||||||
|
@@ -537,6 +537,7 @@
|
|||||||
org.alfresco.service.cmr.ml.MultilingualContentService.getTranslationForLocale=ACL_NODE.0.sys:base.Read
|
org.alfresco.service.cmr.ml.MultilingualContentService.getTranslationForLocale=ACL_NODE.0.sys:base.Read
|
||||||
org.alfresco.service.cmr.ml.MultilingualContentService.getMissingTranslations=ACL_ALLOW
|
org.alfresco.service.cmr.ml.MultilingualContentService.getMissingTranslations=ACL_ALLOW
|
||||||
org.alfresco.service.cmr.ml.MultilingualContentService.getPivotTranslation=ACL_NODE.0.sys:base.Read
|
org.alfresco.service.cmr.ml.MultilingualContentService.getPivotTranslation=ACL_NODE.0.sys:base.Read
|
||||||
|
org.alfresco.service.cmr.ml.MultilingualContentService.isTranslation=ACL_NODE.0.sys:base.Read
|
||||||
org.alfresco.service.cmr.ml.MultilingualContentService.makeTranslation=ACL_NODE.0.sys:base.Write
|
org.alfresco.service.cmr.ml.MultilingualContentService.makeTranslation=ACL_NODE.0.sys:base.Write
|
||||||
org.alfresco.service.cmr.ml.MultilingualContentService.addTranslation=ACL_NODE.0.sys:base.Read,ACL_NODE.1.sys:base.Write
|
org.alfresco.service.cmr.ml.MultilingualContentService.addTranslation=ACL_NODE.0.sys:base.Read,ACL_NODE.1.sys:base.Write
|
||||||
org.alfresco.service.cmr.ml.MultilingualContentService.addEmptyTranslation=ACL_NODE.0.sys:base.Read
|
org.alfresco.service.cmr.ml.MultilingualContentService.addEmptyTranslation=ACL_NODE.0.sys:base.Read
|
||||||
|
@@ -27,13 +27,10 @@ package org.alfresco.repo.content;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.alfresco.error.AlfrescoRuntimeException;
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
import org.alfresco.model.ContentModel;
|
|
||||||
import org.alfresco.repo.avm.AVMNodeConverter;
|
import org.alfresco.repo.avm.AVMNodeConverter;
|
||||||
import org.alfresco.repo.content.ContentServicePolicies.OnContentReadPolicy;
|
import org.alfresco.repo.content.ContentServicePolicies.OnContentReadPolicy;
|
||||||
import org.alfresco.repo.content.ContentServicePolicies.OnContentUpdatePolicy;
|
import org.alfresco.repo.content.ContentServicePolicies.OnContentUpdatePolicy;
|
||||||
@@ -50,7 +47,6 @@ import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
|||||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||||
import org.alfresco.service.cmr.dictionary.InvalidTypeException;
|
import org.alfresco.service.cmr.dictionary.InvalidTypeException;
|
||||||
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
||||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
|
||||||
import org.alfresco.service.cmr.repository.ContentData;
|
import org.alfresco.service.cmr.repository.ContentData;
|
||||||
import org.alfresco.service.cmr.repository.ContentIOException;
|
import org.alfresco.service.cmr.repository.ContentIOException;
|
||||||
import org.alfresco.service.cmr.repository.ContentReader;
|
import org.alfresco.service.cmr.repository.ContentReader;
|
||||||
@@ -63,7 +59,6 @@ import org.alfresco.service.cmr.repository.NodeService;
|
|||||||
import org.alfresco.service.cmr.repository.StoreRef;
|
import org.alfresco.service.cmr.repository.StoreRef;
|
||||||
import org.alfresco.service.namespace.NamespaceService;
|
import org.alfresco.service.namespace.NamespaceService;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
import org.alfresco.service.namespace.RegexQNamePattern;
|
|
||||||
import org.alfresco.service.transaction.TransactionService;
|
import org.alfresco.service.transaction.TransactionService;
|
||||||
import org.alfresco.util.EqualsHelper;
|
import org.alfresco.util.EqualsHelper;
|
||||||
import org.alfresco.util.Pair;
|
import org.alfresco.util.Pair;
|
||||||
@@ -316,66 +311,8 @@ public class RoutingContentService implements ContentService
|
|||||||
// check that the URL is available
|
// check that the URL is available
|
||||||
if (contentData == null || contentData.getContentUrl() == null)
|
if (contentData == null || contentData.getContentUrl() == null)
|
||||||
{
|
{
|
||||||
// if the node is an empty translation, the reader must be the reader of its pivot translation
|
// there is no URL - the interface specifies that this is not an error condition
|
||||||
if(nodeService.hasAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)
|
return null;
|
||||||
&& nodeService.hasAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION))
|
|
||||||
{
|
|
||||||
// NOTE : don't use the MultilingualContentService here because :
|
|
||||||
// A valid SecureContext can't be provided in the RequestContext
|
|
||||||
|
|
||||||
List<ChildAssociationRef> parentAssocRefs = nodeService.getParentAssocs(
|
|
||||||
nodeRef,
|
|
||||||
ContentModel.ASSOC_MULTILINGUAL_CHILD,
|
|
||||||
RegexQNamePattern.MATCH_ALL);
|
|
||||||
|
|
||||||
if (parentAssocRefs.size() == 1)
|
|
||||||
{
|
|
||||||
// Get the ml container and its locale
|
|
||||||
NodeRef container = parentAssocRefs.get(0).getParentRef();
|
|
||||||
Locale containerLocale = (Locale) nodeService.getProperty(container, ContentModel.PROP_LOCALE);
|
|
||||||
|
|
||||||
// Get each translation
|
|
||||||
List<ChildAssociationRef> assocRefs = nodeService.getChildAssocs(
|
|
||||||
container,
|
|
||||||
ContentModel.ASSOC_MULTILINGUAL_CHILD,
|
|
||||||
RegexQNamePattern.MATCH_ALL);
|
|
||||||
|
|
||||||
|
|
||||||
// found the pivot translation
|
|
||||||
NodeRef pivot = null;
|
|
||||||
|
|
||||||
for(ChildAssociationRef assoc : assocRefs)
|
|
||||||
{
|
|
||||||
pivot = assoc.getChildRef();
|
|
||||||
Locale pivotLocale = (Locale) nodeService.getProperty(pivot, ContentModel.PROP_LOCALE);
|
|
||||||
|
|
||||||
if(containerLocale.equals(pivotLocale))
|
|
||||||
{
|
|
||||||
break; // the pivot is set
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pivot = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns the reader of this pivot translation if it's different to
|
|
||||||
// the node in parameter
|
|
||||||
|
|
||||||
if (pivot != null && !nodeRef.getId().equals(pivot.getId()))
|
|
||||||
{
|
|
||||||
return getReader(pivot, propertyQName, fireContentReadPolicy);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// there is no URL - the interface specifies that this is not an error condition
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
String contentUrl = contentData.getContentUrl();
|
String contentUrl = contentData.getContentUrl();
|
||||||
|
|
||||||
|
@@ -30,6 +30,7 @@ import java.util.Collections;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.alfresco.error.AlfrescoRuntimeException;
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
@@ -37,6 +38,7 @@ import org.alfresco.model.ContentModel;
|
|||||||
import org.alfresco.repo.search.QueryParameterDefImpl;
|
import org.alfresco.repo.search.QueryParameterDefImpl;
|
||||||
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
||||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||||
|
import org.alfresco.service.cmr.ml.MultilingualContentService;
|
||||||
import org.alfresco.service.cmr.model.FileExistsException;
|
import org.alfresco.service.cmr.model.FileExistsException;
|
||||||
import org.alfresco.service.cmr.model.FileFolderService;
|
import org.alfresco.service.cmr.model.FileFolderService;
|
||||||
import org.alfresco.service.cmr.model.FileInfo;
|
import org.alfresco.service.cmr.model.FileInfo;
|
||||||
@@ -121,6 +123,7 @@ public class FileFolderServiceImpl implements FileFolderService
|
|||||||
private CopyService copyService;
|
private CopyService copyService;
|
||||||
private SearchService searchService;
|
private SearchService searchService;
|
||||||
private ContentService contentService;
|
private ContentService contentService;
|
||||||
|
private MultilingualContentService multilingualContentService;
|
||||||
private MimetypeService mimetypeService;
|
private MimetypeService mimetypeService;
|
||||||
|
|
||||||
// TODO: Replace this with a more formal means of identifying "system" folders (i.e. aspect or UUID)
|
// TODO: Replace this with a more formal means of identifying "system" folders (i.e. aspect or UUID)
|
||||||
@@ -164,6 +167,11 @@ public class FileFolderServiceImpl implements FileFolderService
|
|||||||
this.contentService = contentService;
|
this.contentService = contentService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setMultilingualContentService(MultilingualContentService multilingualContentService)
|
||||||
|
{
|
||||||
|
this.multilingualContentService = multilingualContentService;
|
||||||
|
}
|
||||||
|
|
||||||
public void setMimetypeService(MimetypeService mimetypeService)
|
public void setMimetypeService(MimetypeService mimetypeService)
|
||||||
{
|
{
|
||||||
this.mimetypeService = mimetypeService;
|
this.mimetypeService = mimetypeService;
|
||||||
@@ -198,7 +206,7 @@ public class FileFolderServiceImpl implements FileFolderService
|
|||||||
List<FileInfo> results = new ArrayList<FileInfo>(nodeRefs.size());
|
List<FileInfo> results = new ArrayList<FileInfo>(nodeRefs.size());
|
||||||
for (NodeRef nodeRef : nodeRefs)
|
for (NodeRef nodeRef : nodeRefs)
|
||||||
{
|
{
|
||||||
FileInfo fileInfo = toFileInfo(nodeRef);
|
FileInfo fileInfo = toFileInfo(nodeRef, true);
|
||||||
results.add(fileInfo);
|
results.add(fileInfo);
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
@@ -207,7 +215,7 @@ public class FileFolderServiceImpl implements FileFolderService
|
|||||||
/**
|
/**
|
||||||
* Helper method to convert a node reference instance to a file info
|
* Helper method to convert a node reference instance to a file info
|
||||||
*/
|
*/
|
||||||
private FileInfo toFileInfo(NodeRef nodeRef) throws InvalidTypeException
|
private FileInfo toFileInfo(NodeRef nodeRef, boolean addTranslations) throws InvalidTypeException
|
||||||
{
|
{
|
||||||
// get the file attributes
|
// get the file attributes
|
||||||
Map<QName, Serializable> properties = nodeService.getProperties(nodeRef);
|
Map<QName, Serializable> properties = nodeService.getProperties(nodeRef);
|
||||||
@@ -215,8 +223,33 @@ public class FileFolderServiceImpl implements FileFolderService
|
|||||||
QName typeQName = nodeService.getType(nodeRef);
|
QName typeQName = nodeService.getType(nodeRef);
|
||||||
boolean isFolder = isFolder(typeQName);
|
boolean isFolder = isFolder(typeQName);
|
||||||
|
|
||||||
|
Map<Locale, FileInfo> translations = null;
|
||||||
|
if (!isFolder && addTranslations)
|
||||||
|
{
|
||||||
|
// Get any translations
|
||||||
|
translations = new HashMap<Locale, FileInfo>(13);
|
||||||
|
// Check for the ML aspect
|
||||||
|
if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT))
|
||||||
|
{
|
||||||
|
// Get all the translations
|
||||||
|
Map<Locale, NodeRef> translationsToConvert = multilingualContentService.getTranslations(nodeRef);
|
||||||
|
for (Map.Entry<Locale, NodeRef> entry : translationsToConvert.entrySet())
|
||||||
|
{
|
||||||
|
Locale locale = entry.getKey();
|
||||||
|
NodeRef nodeRefToConvert = entry.getValue();
|
||||||
|
FileInfo convertedFileInfo = toFileInfo(nodeRefToConvert, false);
|
||||||
|
// Add to map
|
||||||
|
translations.put(locale, convertedFileInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
translations = Collections.<Locale, FileInfo>emptyMap();
|
||||||
|
}
|
||||||
|
|
||||||
// construct the file info and add to the results
|
// construct the file info and add to the results
|
||||||
FileInfo fileInfo = new FileInfoImpl(nodeRef, isFolder, properties);
|
FileInfo fileInfo = new FileInfoImpl(nodeRef, isFolder, properties, translations);
|
||||||
// done
|
// done
|
||||||
return fileInfo;
|
return fileInfo;
|
||||||
}
|
}
|
||||||
@@ -542,7 +575,7 @@ public class FileFolderServiceImpl implements FileFolderService
|
|||||||
private FileInfo moveOrCopy(NodeRef sourceNodeRef, NodeRef targetParentRef, String newName, boolean move) throws FileExistsException, FileNotFoundException
|
private FileInfo moveOrCopy(NodeRef sourceNodeRef, NodeRef targetParentRef, String newName, boolean move) throws FileExistsException, FileNotFoundException
|
||||||
{
|
{
|
||||||
// get file/folder in its current state
|
// get file/folder in its current state
|
||||||
FileInfo beforeFileInfo = toFileInfo(sourceNodeRef);
|
FileInfo beforeFileInfo = toFileInfo(sourceNodeRef, true);
|
||||||
// check the name - null means keep the existing name
|
// check the name - null means keep the existing name
|
||||||
if (newName == null)
|
if (newName == null)
|
||||||
{
|
{
|
||||||
@@ -634,7 +667,7 @@ public class FileFolderServiceImpl implements FileFolderService
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get the details after the operation
|
// get the details after the operation
|
||||||
FileInfo afterFileInfo = toFileInfo(targetNodeRef);
|
FileInfo afterFileInfo = toFileInfo(targetNodeRef, true);
|
||||||
// done
|
// done
|
||||||
if (logger.isDebugEnabled())
|
if (logger.isDebugEnabled())
|
||||||
{
|
{
|
||||||
@@ -705,11 +738,11 @@ public class FileFolderServiceImpl implements FileFolderService
|
|||||||
}
|
}
|
||||||
|
|
||||||
NodeRef nodeRef = assocRef.getChildRef();
|
NodeRef nodeRef = assocRef.getChildRef();
|
||||||
FileInfo fileInfo = toFileInfo(nodeRef);
|
FileInfo fileInfo = toFileInfo(nodeRef, true);
|
||||||
// done
|
// done
|
||||||
if (logger.isDebugEnabled())
|
if (logger.isDebugEnabled())
|
||||||
{
|
{
|
||||||
FileInfo parentFileInfo = toFileInfo(parentNodeRef);
|
FileInfo parentFileInfo = toFileInfo(parentNodeRef, false);
|
||||||
logger.debug("Created: \n" +
|
logger.debug("Created: \n" +
|
||||||
" parent: " + parentFileInfo + "\n" +
|
" parent: " + parentFileInfo + "\n" +
|
||||||
" created: " + fileInfo);
|
" created: " + fileInfo);
|
||||||
@@ -755,7 +788,7 @@ public class FileFolderServiceImpl implements FileFolderService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// done
|
// done
|
||||||
FileInfo fileInfo = toFileInfo(currentParentRef);
|
FileInfo fileInfo = toFileInfo(currentParentRef, true);
|
||||||
return fileInfo;
|
return fileInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -790,7 +823,7 @@ public class FileFolderServiceImpl implements FileFolderService
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// we found the root and expect to be building the path up
|
// we found the root and expect to be building the path up
|
||||||
FileInfo pathInfo = toFileInfo(childNodeRef);
|
FileInfo pathInfo = toFileInfo(childNodeRef, true);
|
||||||
results.add(pathInfo);
|
results.add(pathInfo);
|
||||||
}
|
}
|
||||||
// check that we found the root
|
// check that we found the root
|
||||||
@@ -861,7 +894,7 @@ public class FileFolderServiceImpl implements FileFolderService
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return toFileInfo(nodeRef);
|
return toFileInfo(nodeRef, true);
|
||||||
}
|
}
|
||||||
catch (InvalidTypeException e)
|
catch (InvalidTypeException e)
|
||||||
{
|
{
|
||||||
@@ -871,7 +904,7 @@ public class FileFolderServiceImpl implements FileFolderService
|
|||||||
|
|
||||||
public ContentReader getReader(NodeRef nodeRef)
|
public ContentReader getReader(NodeRef nodeRef)
|
||||||
{
|
{
|
||||||
FileInfo fileInfo = toFileInfo(nodeRef);
|
FileInfo fileInfo = toFileInfo(nodeRef, false);
|
||||||
if (fileInfo.isFolder())
|
if (fileInfo.isFolder())
|
||||||
{
|
{
|
||||||
throw new InvalidTypeException("Unable to get a content reader for a folder: " + fileInfo);
|
throw new InvalidTypeException("Unable to get a content reader for a folder: " + fileInfo);
|
||||||
@@ -881,7 +914,7 @@ public class FileFolderServiceImpl implements FileFolderService
|
|||||||
|
|
||||||
public ContentWriter getWriter(NodeRef nodeRef)
|
public ContentWriter getWriter(NodeRef nodeRef)
|
||||||
{
|
{
|
||||||
FileInfo fileInfo = toFileInfo(nodeRef);
|
FileInfo fileInfo = toFileInfo(nodeRef, false);
|
||||||
if (fileInfo.isFolder())
|
if (fileInfo.isFolder())
|
||||||
{
|
{
|
||||||
throw new InvalidTypeException("Unable to get a content writer for a folder: " + fileInfo);
|
throw new InvalidTypeException("Unable to get a content writer for a folder: " + fileInfo);
|
||||||
|
@@ -25,7 +25,9 @@
|
|||||||
package org.alfresco.repo.model.filefolder;
|
package org.alfresco.repo.model.filefolder;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.alfresco.model.ContentModel;
|
import org.alfresco.model.ContentModel;
|
||||||
@@ -44,6 +46,7 @@ public class FileInfoImpl implements FileInfo
|
|||||||
{
|
{
|
||||||
private NodeRef nodeRef;
|
private NodeRef nodeRef;
|
||||||
private NodeRef linkNodeRef;
|
private NodeRef linkNodeRef;
|
||||||
|
private Map<Locale, FileInfo> translations;
|
||||||
private boolean isFolder;
|
private boolean isFolder;
|
||||||
private boolean isLink;
|
private boolean isLink;
|
||||||
private Map<QName, Serializable> properties;
|
private Map<QName, Serializable> properties;
|
||||||
@@ -52,13 +55,34 @@ public class FileInfoImpl implements FileInfo
|
|||||||
* Package-level constructor
|
* Package-level constructor
|
||||||
*/
|
*/
|
||||||
/* package */ FileInfoImpl(NodeRef nodeRef, boolean isFolder, Map<QName, Serializable> properties)
|
/* package */ FileInfoImpl(NodeRef nodeRef, boolean isFolder, Map<QName, Serializable> properties)
|
||||||
|
{
|
||||||
|
this(nodeRef, isFolder, properties, Collections.<Locale, FileInfo>emptyMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Package-level constructor
|
||||||
|
*
|
||||||
|
* @param translations a map of translations including this instance. It may be null.
|
||||||
|
*/
|
||||||
|
/* package */ FileInfoImpl(
|
||||||
|
NodeRef nodeRef,
|
||||||
|
boolean isFolder,
|
||||||
|
Map<QName, Serializable> properties,
|
||||||
|
Map<Locale, FileInfo> translations)
|
||||||
{
|
{
|
||||||
this.nodeRef = nodeRef;
|
this.nodeRef = nodeRef;
|
||||||
this.isFolder = isFolder;
|
this.isFolder = isFolder;
|
||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
|
if (translations == null || isFolder)
|
||||||
|
{
|
||||||
|
this.translations = Collections.<Locale, FileInfo>emptyMap();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.translations = translations;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if this is a link node
|
// Check if this is a link node
|
||||||
|
|
||||||
if ( properties.containsKey( ContentModel.PROP_LINK_DESTINATION))
|
if ( properties.containsKey( ContentModel.PROP_LINK_DESTINATION))
|
||||||
{
|
{
|
||||||
isLink = true;
|
isLink = true;
|
||||||
@@ -66,6 +90,31 @@ public class FileInfoImpl implements FileInfo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see #getNodeRef()
|
||||||
|
* @see NodeRef#equals(Object)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj)
|
||||||
|
{
|
||||||
|
if (obj == null || this.getClass().isInstance(obj))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
FileInfoImpl that = (FileInfoImpl) obj;
|
||||||
|
return (this.getNodeRef().equals(that.getNodeRef()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see #getNodeRef()
|
||||||
|
* @see NodeRef#hashCode()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
return getNodeRef().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
@@ -81,6 +130,8 @@ public class FileInfoImpl implements FileInfo
|
|||||||
sb.append(linkNodeRef);
|
sb.append(linkNodeRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sb.append(", translations=").append(translations.size());
|
||||||
|
|
||||||
sb.append("]");
|
sb.append("]");
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
@@ -105,6 +156,11 @@ public class FileInfoImpl implements FileInfo
|
|||||||
return linkNodeRef;
|
return linkNodeRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<Locale, FileInfo> getTranslations()
|
||||||
|
{
|
||||||
|
return translations;
|
||||||
|
}
|
||||||
|
|
||||||
public String getName()
|
public String getName()
|
||||||
{
|
{
|
||||||
return (String) properties.get(ContentModel.PROP_NAME);
|
return (String) properties.get(ContentModel.PROP_NAME);
|
||||||
|
@@ -0,0 +1,256 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2007 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
* As a special exception to the terms and conditions of version 2.0 of
|
||||||
|
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||||
|
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||||
|
* FLOSS exception. You should have recieved a copy of the text describing
|
||||||
|
* the FLOSS exception, and it is also available here:
|
||||||
|
* http://www.alfresco.com/legal/licensing"
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.model.filefolder;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.alfresco.i18n.I18NUtil;
|
||||||
|
import org.alfresco.model.ContentModel;
|
||||||
|
import org.alfresco.service.cmr.ml.MultilingualContentService;
|
||||||
|
import org.alfresco.service.cmr.model.FileFolderService;
|
||||||
|
import org.alfresco.service.cmr.model.FileInfo;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
|
import org.aopalliance.intercept.MethodInterceptor;
|
||||||
|
import org.aopalliance.intercept.MethodInvocation;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interceptor that replaces files nodes with their equivalent
|
||||||
|
* translations according to the locale. It is to be used with the
|
||||||
|
* {@link FileFolderService}.
|
||||||
|
*
|
||||||
|
* @since 2.1
|
||||||
|
* @author Derek Hulley
|
||||||
|
*/
|
||||||
|
public class MLTranslationInterceptor implements MethodInterceptor
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Names of methods that return a <code>List</code> or <code>FileInfo</code> instances.
|
||||||
|
*/
|
||||||
|
private static final Set<String> METHOD_NAMES_LIST;
|
||||||
|
/**
|
||||||
|
* Names of methods that return a <code>FileInfo</code>.
|
||||||
|
*/
|
||||||
|
private static final Set<String> METHOD_NAMES_SINGLE;
|
||||||
|
/**
|
||||||
|
* Names of methods that don't need interception. This is used to catch any new methods
|
||||||
|
* added to the interface.
|
||||||
|
*/
|
||||||
|
private static final Set<String> METHOD_NAMES_OTHER;
|
||||||
|
static
|
||||||
|
{
|
||||||
|
METHOD_NAMES_LIST = new HashSet<String>(13);
|
||||||
|
METHOD_NAMES_LIST.add("list");
|
||||||
|
METHOD_NAMES_LIST.add("listFiles");
|
||||||
|
METHOD_NAMES_LIST.add("listFolders");
|
||||||
|
METHOD_NAMES_LIST.add("search");
|
||||||
|
METHOD_NAMES_LIST.add("getNamePath");
|
||||||
|
|
||||||
|
METHOD_NAMES_SINGLE = new HashSet<String>(13);
|
||||||
|
METHOD_NAMES_SINGLE.add("searchSimple");
|
||||||
|
METHOD_NAMES_SINGLE.add("rename");
|
||||||
|
METHOD_NAMES_SINGLE.add("move");
|
||||||
|
METHOD_NAMES_SINGLE.add("copy");
|
||||||
|
METHOD_NAMES_SINGLE.add("create");
|
||||||
|
METHOD_NAMES_SINGLE.add("makeFolders");
|
||||||
|
METHOD_NAMES_SINGLE.add("getNamePath");
|
||||||
|
METHOD_NAMES_SINGLE.add("resolveNamePath");
|
||||||
|
METHOD_NAMES_SINGLE.add("getFileInfo");
|
||||||
|
|
||||||
|
METHOD_NAMES_OTHER = new HashSet<String>(13);
|
||||||
|
METHOD_NAMES_OTHER.add("delete");
|
||||||
|
METHOD_NAMES_OTHER.add("getReader");
|
||||||
|
METHOD_NAMES_OTHER.add("getWriter");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Log logger = LogFactory.getLog(MLTranslationInterceptor.class);
|
||||||
|
|
||||||
|
private NodeService nodeService;
|
||||||
|
private MultilingualContentService multilingualContentService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
public MLTranslationInterceptor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNodeService(NodeService nodeService)
|
||||||
|
{
|
||||||
|
this.nodeService = nodeService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMultilingualContentService(MultilingualContentService multilingualContentService)
|
||||||
|
{
|
||||||
|
this.multilingualContentService = multilingualContentService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the node referenice where an alternative translation should be used.
|
||||||
|
*
|
||||||
|
* @param nodeRef the basic nodeRef
|
||||||
|
* @return Returns the replacement if required
|
||||||
|
*/
|
||||||
|
private NodeRef getTranslatedNodeRef(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
// Ignore null
|
||||||
|
if (nodeRef == null)
|
||||||
|
{
|
||||||
|
return nodeRef;
|
||||||
|
}
|
||||||
|
// Ignore everything without the correct aspect
|
||||||
|
if (!nodeService.hasAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT))
|
||||||
|
{
|
||||||
|
return nodeRef;
|
||||||
|
}
|
||||||
|
// Find the translation
|
||||||
|
Map<Locale, NodeRef> translations = multilingualContentService.getTranslations(nodeRef);
|
||||||
|
Locale filterLocale = I18NUtil.getContentLocaleOrNull();
|
||||||
|
Set<Locale> possibleLocales = translations.keySet();
|
||||||
|
Locale localeToUse = I18NUtil.getNearestLocale(filterLocale, possibleLocales);
|
||||||
|
// Select the node
|
||||||
|
NodeRef translatedNodeRef = translations.get(localeToUse);
|
||||||
|
// Done
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
if (nodeRef.equals(translatedNodeRef))
|
||||||
|
{
|
||||||
|
logger.debug("NodeRef substitution: " + nodeRef + " --> " + translatedNodeRef);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.debug("NodeRef substitution: " + nodeRef + " (no change)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nodeRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the file info where an alternative translation should be used.
|
||||||
|
*
|
||||||
|
* @param fileInfo the basic file or folder info
|
||||||
|
* @return Returns a replacement if required
|
||||||
|
*
|
||||||
|
* @see FileInfo#getTranslations()
|
||||||
|
*/
|
||||||
|
private FileInfo getTranslatedFileInfo(FileInfo fileInfo)
|
||||||
|
{
|
||||||
|
// Ignore null
|
||||||
|
if (fileInfo == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Ignore folders
|
||||||
|
if (fileInfo.isFolder())
|
||||||
|
{
|
||||||
|
return fileInfo;
|
||||||
|
}
|
||||||
|
// Ignore files without translations
|
||||||
|
Map<Locale, FileInfo> translations = fileInfo.getTranslations();
|
||||||
|
if (translations.size() == 0)
|
||||||
|
{
|
||||||
|
return fileInfo;
|
||||||
|
}
|
||||||
|
// Get the locale to use
|
||||||
|
Set<Locale> possibleLocales = translations.keySet();
|
||||||
|
Locale filterLocale = I18NUtil.getContentLocaleOrNull();
|
||||||
|
Locale localeToUse = I18NUtil.getNearestLocale(filterLocale, possibleLocales);
|
||||||
|
FileInfo translatedFileInfo = translations.get(localeToUse);
|
||||||
|
// Done
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
if (fileInfo.equals(translatedFileInfo))
|
||||||
|
{
|
||||||
|
logger.debug("FileInfo substitution: " + fileInfo + " --> " + translatedFileInfo);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.debug("FileInfo substitution: " + fileInfo + " (no change)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return translatedFileInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Object invoke(MethodInvocation invocation) throws Throwable
|
||||||
|
{
|
||||||
|
Object ret = null;
|
||||||
|
String methodName = invocation.getMethod().getName();
|
||||||
|
|
||||||
|
if (METHOD_NAMES_LIST.contains(methodName))
|
||||||
|
{
|
||||||
|
List<FileInfo> fileInfos = (List<FileInfo>) invocation.proceed();
|
||||||
|
// Compile a set to ensure we don't get duplicates
|
||||||
|
Map<FileInfo, FileInfo> translatedFileInfos = new HashMap<FileInfo, FileInfo>(17);
|
||||||
|
for (FileInfo fileInfo : fileInfos)
|
||||||
|
{
|
||||||
|
FileInfo translatedFileInfo = getTranslatedFileInfo(fileInfo);
|
||||||
|
// Add this to the set
|
||||||
|
translatedFileInfos.put(fileInfo, translatedFileInfo);
|
||||||
|
}
|
||||||
|
// Convert the set back to a list
|
||||||
|
List<FileInfo> orderedResults = new ArrayList<FileInfo>(fileInfos.size());
|
||||||
|
for (FileInfo info : fileInfos)
|
||||||
|
{
|
||||||
|
orderedResults.add(translatedFileInfos.get(info));
|
||||||
|
}
|
||||||
|
ret = orderedResults;
|
||||||
|
}
|
||||||
|
else if (METHOD_NAMES_SINGLE.contains(methodName))
|
||||||
|
{
|
||||||
|
Object obj = invocation.proceed();
|
||||||
|
if (obj instanceof FileInfo)
|
||||||
|
{
|
||||||
|
FileInfo fileInfo = (FileInfo) obj;
|
||||||
|
ret = getTranslatedFileInfo(fileInfo);
|
||||||
|
}
|
||||||
|
else if (obj instanceof NodeRef)
|
||||||
|
{
|
||||||
|
NodeRef nodeRef = (NodeRef) obj;
|
||||||
|
ret = getTranslatedNodeRef(nodeRef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (METHOD_NAMES_OTHER.contains(methodName))
|
||||||
|
{
|
||||||
|
// There is nothing to do
|
||||||
|
ret = invocation.proceed();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new RuntimeException("Method not handled by interceptor: " + methodName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
@@ -50,8 +50,8 @@ import org.alfresco.service.namespace.QName;
|
|||||||
*/
|
*/
|
||||||
public class EmptyTranslationAspect implements
|
public class EmptyTranslationAspect implements
|
||||||
CopyServicePolicies.OnCopyNodePolicy,
|
CopyServicePolicies.OnCopyNodePolicy,
|
||||||
NodeServicePolicies.BeforeDeleteNodePolicy,
|
// NodeServicePolicies.BeforeDeleteNodePolicy,
|
||||||
NodeServicePolicies.OnRemoveAspectPolicy,
|
// NodeServicePolicies.OnRemoveAspectPolicy,
|
||||||
ContentServicePolicies.OnContentUpdatePolicy
|
ContentServicePolicies.OnContentUpdatePolicy
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -77,16 +77,16 @@ public class EmptyTranslationAspect implements
|
|||||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onContentUpdate"),
|
QName.createQName(NamespaceService.ALFRESCO_URI, "onContentUpdate"),
|
||||||
ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION,
|
ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION,
|
||||||
new JavaBehaviour(this, "onContentUpdate"));
|
new JavaBehaviour(this, "onContentUpdate"));
|
||||||
|
//
|
||||||
this.policyComponent.bindClassBehaviour(
|
// this.policyComponent.bindClassBehaviour(
|
||||||
QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"),
|
// QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"),
|
||||||
ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION,
|
// ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION,
|
||||||
new JavaBehaviour(this, "beforeDeleteNode"));
|
// new JavaBehaviour(this, "beforeDeleteNode"));
|
||||||
|
//
|
||||||
this.policyComponent.bindClassBehaviour(
|
// this.policyComponent.bindClassBehaviour(
|
||||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onRemoveAspect"),
|
// QName.createQName(NamespaceService.ALFRESCO_URI, "onRemoveAspect"),
|
||||||
ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION,
|
// ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION,
|
||||||
new JavaBehaviour(this, "onRemoveAspect"));
|
// new JavaBehaviour(this, "onRemoveAspect"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -123,38 +123,38 @@ public class EmptyTranslationAspect implements
|
|||||||
*/
|
*/
|
||||||
public void onContentUpdate(NodeRef nodeRef, boolean newContent)
|
public void onContentUpdate(NodeRef nodeRef, boolean newContent)
|
||||||
{
|
{
|
||||||
if(newContent)
|
if (newContent)
|
||||||
{
|
{
|
||||||
nodeService.removeAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION);
|
nodeService.removeAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION);
|
||||||
|
nodeService.removeAspect(nodeRef, ContentModel.ASPECT_TEMPORARY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* If a <b>cm:mlEmptyTranslation<b> is deleted, it can't be archived.
|
// * If a <b>cm:mlEmptyTranslation<b> is deleted, it can't be archived.
|
||||||
*
|
// *
|
||||||
* @see org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy#beforeDeleteNode(org.alfresco.service.cmr.repository.NodeRef)
|
// * @see org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy#beforeDeleteNode(org.alfresco.service.cmr.repository.NodeRef)
|
||||||
*/
|
// */
|
||||||
public void beforeDeleteNode(NodeRef nodeRef)
|
// public void beforeDeleteNode(NodeRef nodeRef)
|
||||||
{
|
// {
|
||||||
// add TEMPORARY ASPECT to force the deleteNode
|
// // add TEMPORARY ASPECT to force the deleteNode
|
||||||
nodeService.addAspect(nodeRef, ContentModel.ASPECT_TEMPORARY, null);
|
// nodeService.addAspect(nodeRef, ContentModel.ASPECT_TEMPORARY, null);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* If the aspect <b>cm:mlEmptyTranslation<b> is removed <b>and the content url property is null</b>, the node can't be store much time.
|
// * If the aspect <b>cm:mlEmptyTranslation<b> is removed <b>and the content url property is null</b>, the node can be deleted.
|
||||||
*
|
// * The other time the aspect is removed is when new content is added, in which case the node must be kept.
|
||||||
* @see org.alfresco.repo.node.NodeServicePolicies.OnRemoveAspectPolicy#onRemoveAspect(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) *
|
// *
|
||||||
*/
|
// * @see org.alfresco.repo.node.NodeServicePolicies.OnRemoveAspectPolicy#onRemoveAspect(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) *
|
||||||
public void onRemoveAspect(NodeRef nodeRef, QName aspectTypeQName)
|
// */
|
||||||
{
|
// public void onRemoveAspect(NodeRef nodeRef, QName aspectTypeQName)
|
||||||
// if the node is updated, the URL will not be null and the rome aspect don't delete
|
// {
|
||||||
// the translation. It will be a simple mlDocument.
|
// // Delete the node if the content is empty.
|
||||||
if(aspectTypeQName.equals(ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION)
|
// // Keep the node if it has content
|
||||||
&& ((ContentData) nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT)).getContentUrl() == null)
|
// ContentData contentData = (ContentData) nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT);
|
||||||
{
|
// if(contentData.getContentUrl() == null)
|
||||||
// add TEMPORARY ASPECT to force the deleteNode
|
// {
|
||||||
nodeService.addAspect(nodeRef, ContentModel.ASPECT_TEMPORARY, null);
|
// nodeService.deleteNode(nodeRef);
|
||||||
nodeService.deleteNode(nodeRef);
|
// }
|
||||||
}
|
// }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -118,7 +118,7 @@ public class MLContainerType implements
|
|||||||
Map<Locale, NodeRef> translations = multilingualContentService.getTranslations(nodeRef);
|
Map<Locale, NodeRef> translations = multilingualContentService.getTranslations(nodeRef);
|
||||||
|
|
||||||
// add the DELETION_RUNNING property
|
// add the DELETION_RUNNING property
|
||||||
nodeService.setProperty(nodeRef, PROP_NAME_DELETION_RUNNING, new Boolean(true));
|
nodeService.setProperty(nodeRef, PROP_NAME_DELETION_RUNNING, Boolean.TRUE);
|
||||||
|
|
||||||
for(Map.Entry<Locale, NodeRef> entry : translations.entrySet())
|
for(Map.Entry<Locale, NodeRef> entry : translations.entrySet())
|
||||||
{
|
{
|
||||||
@@ -126,10 +126,7 @@ public class MLContainerType implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remove the DELETION_RUNNING property
|
// remove the DELETION_RUNNING property
|
||||||
Map<QName, Serializable> prop = nodeService.getProperties(nodeRef);
|
nodeService.removeProperty(nodeRef, PROP_NAME_DELETION_RUNNING);
|
||||||
prop.remove(PROP_NAME_DELETION_RUNNING);
|
|
||||||
nodeService.setProperties(nodeRef, prop);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -245,6 +245,42 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic
|
|||||||
return mlContainerNodeRef;
|
return mlContainerNodeRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
public boolean isTranslation(NodeRef contentNodeRef)
|
||||||
|
{
|
||||||
|
if (!nodeService.hasAspect(contentNodeRef, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT))
|
||||||
|
{
|
||||||
|
// It doesn't have the aspect, so it isn't a translation
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Document is not multilingual: " + contentNodeRef);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Is there a ML container
|
||||||
|
List<ChildAssociationRef> parentAssocRefs = nodeService.getParentAssocs(
|
||||||
|
contentNodeRef,
|
||||||
|
ContentModel.ASSOC_MULTILINGUAL_CHILD,
|
||||||
|
RegexQNamePattern.MATCH_ALL);
|
||||||
|
if (parentAssocRefs.size() > 0)
|
||||||
|
{
|
||||||
|
// It has the parent required
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Document has ML container parent: " + contentNodeRef);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Document has no ML container parent: " + contentNodeRef);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
public NodeRef makeTranslation(NodeRef contentNodeRef, Locale locale)
|
public NodeRef makeTranslation(NodeRef contentNodeRef, Locale locale)
|
||||||
{
|
{
|
||||||
@@ -505,7 +541,11 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc */
|
* @inheritDoc
|
||||||
|
*
|
||||||
|
* TODO: This logic merely creates a file with a specific aspect and is designed to support
|
||||||
|
* specific use-case in the UI. Examine if the logic should be here or in the UI.
|
||||||
|
*/
|
||||||
public NodeRef addEmptyTranslation(NodeRef translationOfNodeRef, String name, Locale locale)
|
public NodeRef addEmptyTranslation(NodeRef translationOfNodeRef, String name, Locale locale)
|
||||||
{
|
{
|
||||||
// any node used as reference
|
// any node used as reference
|
||||||
@@ -542,8 +582,11 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic
|
|||||||
|
|
||||||
// set it empty
|
// set it empty
|
||||||
nodeService.addAspect(newTranslationNodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION, null);
|
nodeService.addAspect(newTranslationNodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION, null);
|
||||||
|
// Initially, the file should be temporary. This will be changed as soon as some content is added.
|
||||||
|
nodeService.addAspect(newTranslationNodeRef, ContentModel.ASPECT_TEMPORARY, null);
|
||||||
|
|
||||||
// get the extension and set the ContentData property with an null URL.
|
// get the extension and set the ContentData property with an null URL.
|
||||||
|
// TODO: Mimetype must be correct, i.e. taken from the original
|
||||||
String extension = "";
|
String extension = "";
|
||||||
int dotIdx;
|
int dotIdx;
|
||||||
if((dotIdx = name.lastIndexOf(".")) > -1 )
|
if((dotIdx = name.lastIndexOf(".")) > -1 )
|
||||||
|
@@ -141,12 +141,18 @@ public class MultilingualContentServiceImplTest extends TestCase
|
|||||||
public void testMakeTranslation() throws Exception
|
public void testMakeTranslation() throws Exception
|
||||||
{
|
{
|
||||||
NodeRef contentNodeRef = createContent();
|
NodeRef contentNodeRef = createContent();
|
||||||
|
// Check that it is not a translation
|
||||||
|
boolean isTranslation = multilingualContentService.isTranslation(contentNodeRef);
|
||||||
|
assertFalse("New content should not be a translation", isTranslation);
|
||||||
// Turn the content into a translation with the appropriate structures
|
// Turn the content into a translation with the appropriate structures
|
||||||
NodeRef mlContainerNodeRef = multilingualContentService.makeTranslation(contentNodeRef, Locale.CHINESE);
|
NodeRef mlContainerNodeRef = multilingualContentService.makeTranslation(contentNodeRef, Locale.CHINESE);
|
||||||
// Check it
|
// Check it
|
||||||
assertNotNull("Container not created", mlContainerNodeRef);
|
assertNotNull("Container not created", mlContainerNodeRef);
|
||||||
// Check the container child count
|
// Check the container child count
|
||||||
assertEquals("Incorrect number of child nodes", 1, nodeService.getChildAssocs(mlContainerNodeRef).size());
|
assertEquals("Incorrect number of child nodes", 1, nodeService.getChildAssocs(mlContainerNodeRef).size());
|
||||||
|
// Check that it registers as a translation
|
||||||
|
isTranslation = multilingualContentService.isTranslation(contentNodeRef);
|
||||||
|
assertTrue("Content should be a translation", isTranslation);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAddTranslationUsingContainer() throws Exception
|
public void testAddTranslationUsingContainer() throws Exception
|
||||||
|
@@ -149,21 +149,16 @@ public class MultilingualDocumentAspect implements
|
|||||||
*/
|
*/
|
||||||
public void onCopyComplete(QName classRef, NodeRef sourceNodeRef, NodeRef destinationRef, boolean copyToNewNode, Map<NodeRef, NodeRef> copyMap)
|
public void onCopyComplete(QName classRef, NodeRef sourceNodeRef, NodeRef destinationRef, boolean copyToNewNode, Map<NodeRef, NodeRef> copyMap)
|
||||||
{
|
{
|
||||||
Map<QName, Serializable> copiedProp = nodeService.getProperties(destinationRef);
|
nodeService.removeProperty(destinationRef, ContentModel.PROP_LOCALE);
|
||||||
copiedProp.remove(ContentModel.PROP_LOCALE);
|
|
||||||
nodeService.setProperties(destinationRef, copiedProp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A <b>cm:mlDocument</b> is pivot translation it is a multilingual document (non empty) if it's language matches the language
|
* If the node is multilingual and it is the pivot translation, then deletion is not allowed unless the
|
||||||
* of its <b>cm:mlDocument</b>. And a pivot translation can't be removed if it's not the last translation of the <b>cm:mlContainer</b>.
|
|
||||||
*
|
|
||||||
* If a translation is deleted, it's multilingual aspect is lost.
|
|
||||||
*
|
|
||||||
* @see org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy#beforeDeleteNode(org.alfresco.service.cmr.repository.NodeRef)
|
|
||||||
*/
|
*/
|
||||||
public void beforeDeleteNode(NodeRef nodeRef)
|
public void beforeDeleteNode(NodeRef nodeRef)
|
||||||
{
|
{
|
||||||
|
// checkRemoveParentMLContainer(nodeRef);
|
||||||
|
// Remove the aspect
|
||||||
nodeService.removeAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT);
|
nodeService.removeAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,60 +173,52 @@ public class MultilingualDocumentAspect implements
|
|||||||
*/
|
*/
|
||||||
public void beforeRemoveAspect(NodeRef nodeRef, QName aspectTypeQName)
|
public void beforeRemoveAspect(NodeRef nodeRef, QName aspectTypeQName)
|
||||||
{
|
{
|
||||||
if(aspectTypeQName.equals(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT))
|
checkRemoveParentMLContainer(nodeRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkRemoveParentMLContainer(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
// Avoid nodes that are no longer translations
|
||||||
|
if (!multilingualContentService.isTranslation(nodeRef))
|
||||||
{
|
{
|
||||||
NodeRef mlContainer = multilingualContentService.getTranslationContainer(nodeRef);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// nothing to do if the mlContainer is in a deletion process
|
NodeRef mlContainer = multilingualContentService.getTranslationContainer(nodeRef);
|
||||||
Boolean inDeleteProcess = (Boolean) nodeService.getProperty(mlContainer, MLContainerType.PROP_NAME_DELETION_RUNNING);
|
|
||||||
if(inDeleteProcess != null && inDeleteProcess == true)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Locale mlContainerLocale = (Locale) nodeService.getProperty(mlContainer, ContentModel.PROP_LOCALE);
|
// nothing to do if the mlContainer is in a deletion process
|
||||||
Locale nodeRefLocale = (Locale) nodeService.getProperty(nodeRef, ContentModel.PROP_LOCALE);
|
Boolean inDeleteProcess = (Boolean) nodeService.getProperty(mlContainer, MLContainerType.PROP_NAME_DELETION_RUNNING);
|
||||||
|
if(inDeleteProcess != null && inDeleteProcess == true)
|
||||||
|
{
|
||||||
|
// TODO: Is this still called? Can we get rid of the DELETION_RUNNING property?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
nodeService.removeChild(mlContainer, nodeRef);
|
Locale mlContainerLocale = (Locale) nodeService.getProperty(mlContainer, ContentModel.PROP_LOCALE);
|
||||||
|
Locale nodeRefLocale = (Locale) nodeService.getProperty(nodeRef, ContentModel.PROP_LOCALE);
|
||||||
|
|
||||||
// if last translation or if nodeRef is pivot translation
|
nodeService.removeChild(mlContainer, nodeRef);
|
||||||
if(multilingualContentService.getTranslations(mlContainer).size() == 0
|
|
||||||
|| mlContainerLocale.equals(nodeRefLocale))
|
// if last translation or if nodeRef is pivot translation
|
||||||
{
|
if (multilingualContentService.getTranslations(mlContainer).size() == 0
|
||||||
// delete the mlContainer
|
|| mlContainerLocale.equals(nodeRefLocale))
|
||||||
nodeService.deleteNode(mlContainer);
|
{
|
||||||
}
|
// delete the mlContainer
|
||||||
|
nodeService.deleteNode(mlContainer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* After removing the <b>cm:mlDocument</b> aspect :
|
* Removes the document's locale and the <b>cm:mlEmptyTranslation</b> aspect,
|
||||||
* - the node is removed is it's a <b>cm:mlEmptyTranslation</b>
|
* if present.
|
||||||
* - if not, only the locale property is removed
|
|
||||||
*
|
|
||||||
* @see org.alfresco.repo.node.NodeServicePolicies.OnRemoveAspectPolicy#onRemoveAspect(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName)
|
|
||||||
*/
|
*/
|
||||||
public void onRemoveAspect(NodeRef nodeRef, QName aspectTypeQName)
|
public void onRemoveAspect(NodeRef nodeRef, QName aspectTypeQName)
|
||||||
{
|
{
|
||||||
if(aspectTypeQName.equals(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT))
|
nodeService.removeProperty(nodeRef, ContentModel.PROP_LOCALE);
|
||||||
|
// Remove the empty translation aspect if it is present
|
||||||
|
if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION))
|
||||||
{
|
{
|
||||||
|
nodeService.removeAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION);
|
||||||
if(nodeService.hasAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION))
|
|
||||||
{
|
|
||||||
// note: if the node has the temporary aspect and the mlEmptyTranslation in a same
|
|
||||||
// time, it means that the node is being deleted by another process...
|
|
||||||
if(!nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TEMPORARY))
|
|
||||||
{
|
|
||||||
nodeService.deleteNode(nodeRef);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Map<QName, Serializable> props = nodeService.getProperties(nodeRef);
|
|
||||||
props.remove(ContentModel.PROP_LOCALE);
|
|
||||||
nodeService.setProperties(nodeRef, props);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -119,8 +119,6 @@ public class EmptyTranslationAspectTest extends AbstractMultilingualTestCases {
|
|||||||
|
|
||||||
fileFolderService.getWriter(pivot).putContent(contentString);
|
fileFolderService.getWriter(pivot).putContent(contentString);
|
||||||
|
|
||||||
// Ensure that the URL property of the empty translation content is null
|
|
||||||
assertNull("The URL property of an empty translation must be null", ((ContentData) nodeService.getProperty(empty, ContentModel.PROP_CONTENT)).getContentUrl());
|
|
||||||
// Ensure that the content retourned by a get reader of the empty document is the same as the pivot
|
// Ensure that the content retourned by a get reader of the empty document is the same as the pivot
|
||||||
assertEquals("The content retourned of the empty translation must be the same that the content of the pivot",
|
assertEquals("The content retourned of the empty translation must be the same that the content of the pivot",
|
||||||
fileFolderService.getReader(pivot).getContentString(),
|
fileFolderService.getReader(pivot).getContentString(),
|
||||||
|
@@ -82,15 +82,13 @@ public class MultilingualDocumentAspectTest extends AbstractMultilingualTestCase
|
|||||||
NodeRef restoredNode = nodeArchiveService.restoreArchivedNode(nodeArchiveService.getArchivedNode(trad3)).getRestoredNodeRef();
|
NodeRef restoredNode = nodeArchiveService.restoreArchivedNode(nodeArchiveService.getArchivedNode(trad3)).getRestoredNodeRef();
|
||||||
|
|
||||||
// Ensure that the restored node is restored to it s original space
|
// Ensure that the restored node is restored to it s original space
|
||||||
assertEquals("The restaured node must be restaured to the the space", 3, nodeService.getChildAssocs(parent).size());
|
assertEquals("The restored node must be restaured to the the space", 3, nodeService.getChildAssocs(parent).size());
|
||||||
// Ensure that the restored node is not linked to the mlContainer
|
// Ensure that the restored node is not linked to the mlContainer
|
||||||
assertEquals("The restaured node would not be restaured to the mlContainer", 2, multilingualContentService.getTranslations(mlContainer).size());
|
assertEquals("The restored node would not be restaured to the mlContainer", 2, multilingualContentService.getTranslations(mlContainer).size());
|
||||||
// Ensure that the restored node doesn't keep the mlDocument aspect
|
// Ensure that the restored node doesn't keep the mlDocument aspect
|
||||||
assertFalse("The restaured node can't keep the multilingual aspect", nodeService.hasAspect(restoredNode, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT));
|
assertFalse("The restored node can't keep the multilingual aspect", nodeService.hasAspect(restoredNode, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT));
|
||||||
// Ensure that the restored node doesn't keep the locale property
|
// Ensure that the restored node doesn't keep the locale property
|
||||||
assertNull("The restaured node can't keep the locale property", nodeService.getProperty(restoredNode, ContentModel.PROP_LOCALE));
|
assertNull("The restaured node can't keep the locale property", nodeService.getProperty(restoredNode, ContentModel.PROP_LOCALE));
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testDeletePivot() throws Exception
|
public void testDeletePivot() throws Exception
|
||||||
|
@@ -30,9 +30,11 @@ import java.util.Locale;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.alfresco.i18n.I18NUtil;
|
import org.alfresco.i18n.I18NUtil;
|
||||||
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
||||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||||
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
||||||
|
import org.alfresco.service.cmr.ml.MultilingualContentService;
|
||||||
import org.alfresco.service.cmr.repository.MLText;
|
import org.alfresco.service.cmr.repository.MLText;
|
||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
import org.alfresco.service.cmr.repository.NodeService;
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
@@ -72,6 +74,8 @@ public class MLPropertyInterceptor implements MethodInterceptor
|
|||||||
|
|
||||||
/** Direct access to the NodeService */
|
/** Direct access to the NodeService */
|
||||||
private NodeService directNodeService;
|
private NodeService directNodeService;
|
||||||
|
/** Direct access to the ML Content Service */
|
||||||
|
private MultilingualContentService directMultilingualContentService;
|
||||||
/** Used to access property definitions */
|
/** Used to access property definitions */
|
||||||
private DictionaryService dictionaryService;
|
private DictionaryService dictionaryService;
|
||||||
|
|
||||||
@@ -109,6 +113,11 @@ public class MLPropertyInterceptor implements MethodInterceptor
|
|||||||
this.directNodeService = bean;
|
this.directNodeService = bean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setDirectMultilingualContentService(MultilingualContentService directMultilingualContentService)
|
||||||
|
{
|
||||||
|
this.directMultilingualContentService = directMultilingualContentService;
|
||||||
|
}
|
||||||
|
|
||||||
public void setDictionaryService(DictionaryService dictionaryService)
|
public void setDictionaryService(DictionaryService dictionaryService)
|
||||||
{
|
{
|
||||||
this.dictionaryService = dictionaryService;
|
this.dictionaryService = dictionaryService;
|
||||||
@@ -139,43 +148,26 @@ public class MLPropertyInterceptor implements MethodInterceptor
|
|||||||
|
|
||||||
if (methodName.equals("getProperty"))
|
if (methodName.equals("getProperty"))
|
||||||
{
|
{
|
||||||
ret = invocation.proceed();
|
NodeRef nodeRef = (NodeRef) args[0];
|
||||||
// The return value might need to be converted to a String
|
QName propertyQName = (QName) args[1];
|
||||||
if (ret != null && ret instanceof MLText)
|
|
||||||
{
|
Serializable value = (Serializable) invocation.proceed();
|
||||||
MLText mlText = (MLText) ret;
|
ret = convertOutboundProperty(contentLocale, nodeRef, propertyQName, value);
|
||||||
ret = mlText.getClosestValue(contentLocale);
|
|
||||||
// done
|
|
||||||
if (logger.isDebugEnabled())
|
|
||||||
{
|
|
||||||
logger.debug(
|
|
||||||
"Converted ML text: \n" +
|
|
||||||
" initial: " + mlText + "\n" +
|
|
||||||
" converted: " + ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (methodName.equals("getProperties"))
|
else if (methodName.equals("getProperties"))
|
||||||
{
|
{
|
||||||
|
NodeRef nodeRef = (NodeRef) args[0];
|
||||||
|
|
||||||
Map<QName, Serializable> properties = (Map<QName, Serializable>) invocation.proceed();
|
Map<QName, Serializable> properties = (Map<QName, Serializable>) invocation.proceed();
|
||||||
Map<QName, Serializable> convertedProperties = new HashMap<QName, Serializable>(properties.size() * 2);
|
Map<QName, Serializable> convertedProperties = new HashMap<QName, Serializable>(properties.size() * 2);
|
||||||
// Check each return value type
|
// Check each return value type
|
||||||
for (Map.Entry<QName, Serializable> entry : properties.entrySet())
|
for (Map.Entry<QName, Serializable> entry : properties.entrySet())
|
||||||
{
|
{
|
||||||
QName key = entry.getKey();
|
QName propertyQName = entry.getKey();
|
||||||
Serializable value = entry.getValue();
|
Serializable value = entry.getValue();
|
||||||
if (value != null && value instanceof MLText)
|
Serializable convertedValue = convertOutboundProperty(contentLocale, nodeRef, propertyQName, value);
|
||||||
{
|
// Add it to the return map
|
||||||
MLText mlText = (MLText) value;
|
convertedProperties.put(propertyQName, convertedValue);
|
||||||
value = mlText.getClosestValue(contentLocale);
|
|
||||||
// Store the converted value
|
|
||||||
convertedProperties.put(key, value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// The value goes straight back in
|
|
||||||
convertedProperties.put(key, value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ret = convertedProperties;
|
ret = convertedProperties;
|
||||||
// Done
|
// Done
|
||||||
@@ -212,7 +204,6 @@ public class MLPropertyInterceptor implements MethodInterceptor
|
|||||||
}
|
}
|
||||||
else if (methodName.equals("setProperty"))
|
else if (methodName.equals("setProperty"))
|
||||||
{
|
{
|
||||||
//check if the property is of type MLText
|
|
||||||
NodeRef nodeRef = (NodeRef) args[0];
|
NodeRef nodeRef = (NodeRef) args[0];
|
||||||
QName propertyQName = (QName) args[1];
|
QName propertyQName = (QName) args[1];
|
||||||
Serializable inboundValue = (Serializable) args[2];
|
Serializable inboundValue = (Serializable) args[2];
|
||||||
@@ -232,6 +223,64 @@ public class MLPropertyInterceptor implements MethodInterceptor
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure that content is spoofed for empty translations.
|
||||||
|
*/
|
||||||
|
private Serializable convertOutboundProperty(
|
||||||
|
Locale contentLocale,
|
||||||
|
NodeRef nodeRef,
|
||||||
|
QName propertyQName,
|
||||||
|
Serializable outboundValue)
|
||||||
|
{
|
||||||
|
Serializable ret = null;
|
||||||
|
PropertyDefinition propertyDef = this.dictionaryService.getProperty(propertyQName);
|
||||||
|
// Is it content?
|
||||||
|
if (propertyDef != null && propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))
|
||||||
|
{
|
||||||
|
// Check if the document is an empty translation
|
||||||
|
if (directNodeService.hasAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION))
|
||||||
|
{
|
||||||
|
// Ignore the value and take it directly from the pivot translation
|
||||||
|
NodeRef pivotNodeRef = directMultilingualContentService.getPivotTranslation(nodeRef);
|
||||||
|
if (pivotNodeRef == null)
|
||||||
|
{
|
||||||
|
// This is very bad, but we don't fail the server for it
|
||||||
|
logger.warn("No pivot translation found for empty translation: " + nodeRef);
|
||||||
|
ret = outboundValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Get the corresponding property from the pivot
|
||||||
|
ret = directNodeService.getProperty(pivotNodeRef, propertyQName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = outboundValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (outboundValue != null && outboundValue instanceof MLText)
|
||||||
|
{
|
||||||
|
MLText mlText = (MLText) outboundValue;
|
||||||
|
ret = mlText.getClosestValue(contentLocale);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = outboundValue;
|
||||||
|
}
|
||||||
|
// Done
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug(
|
||||||
|
"Converted outbound property: \n" +
|
||||||
|
" NodeRef: " + nodeRef + "\n" +
|
||||||
|
" Property: " + propertyQName + "\n" +
|
||||||
|
" Before: " + outboundValue + "\n" +
|
||||||
|
" After: " + ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param inboundValue The value that must be set
|
* @param inboundValue The value that must be set
|
||||||
|
@@ -1,172 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2005-2007 Alfresco Software Limited.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
|
|
||||||
* As a special exception to the terms and conditions of version 2.0 of
|
|
||||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
|
||||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
|
||||||
* FLOSS exception. You should have recieved a copy of the text describing
|
|
||||||
* the FLOSS exception, and it is also available here:
|
|
||||||
* http://www.alfresco.com/legal/licensing"
|
|
||||||
*/
|
|
||||||
package org.alfresco.repo.node;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.alfresco.i18n.I18NUtil;
|
|
||||||
import org.alfresco.model.ContentModel;
|
|
||||||
import org.alfresco.service.cmr.ml.MultilingualContentService;
|
|
||||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
|
||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
|
||||||
import org.alfresco.service.cmr.repository.NodeService;
|
|
||||||
import org.alfresco.service.namespace.QName;
|
|
||||||
import org.aopalliance.intercept.MethodInterceptor;
|
|
||||||
import org.aopalliance.intercept.MethodInvocation;
|
|
||||||
import org.apache.commons.logging.Log;
|
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interceptor to
|
|
||||||
* - filter the multilingual nodes to display the documents in the prefered language of teh user
|
|
||||||
*
|
|
||||||
* @author yanipig
|
|
||||||
*/
|
|
||||||
public class MLTranslationInterceptor implements MethodInterceptor
|
|
||||||
{
|
|
||||||
private static Log logger = LogFactory
|
|
||||||
.getLog(MLTranslationInterceptor.class);
|
|
||||||
|
|
||||||
private NodeService directNodeService;
|
|
||||||
|
|
||||||
private MultilingualContentService multilingualContentService;
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public Object invoke(MethodInvocation invocation) throws Throwable
|
|
||||||
{
|
|
||||||
Object ret = null;
|
|
||||||
String methodName = invocation.getMethod().getName();
|
|
||||||
|
|
||||||
// intercept the methods getChildAssocs and getChildByNames to apply filter.
|
|
||||||
if (methodName.equals("getChildAssocs") || methodName.equals("getChildByName"))
|
|
||||||
{
|
|
||||||
ret = invocation.proceed();
|
|
||||||
|
|
||||||
NodeRef parent = (NodeRef) invocation.getArguments()[0];
|
|
||||||
|
|
||||||
// all the association returned by the method
|
|
||||||
List<ChildAssociationRef> allChildAssoc = (List<ChildAssociationRef>) ret;
|
|
||||||
|
|
||||||
// get the user content filter language
|
|
||||||
Locale filterLocale = I18NUtil.getContentLocaleOrNull();
|
|
||||||
|
|
||||||
if(filterLocale != null
|
|
||||||
&& directNodeService.getType(parent).equals(ContentModel.TYPE_FOLDER)
|
|
||||||
&& ret != null
|
|
||||||
&& !allChildAssoc.isEmpty()
|
|
||||||
)
|
|
||||||
{
|
|
||||||
|
|
||||||
// the list of Association to return
|
|
||||||
List<ChildAssociationRef> toReturn = new ArrayList();
|
|
||||||
// the ml containers found in the folder
|
|
||||||
List<NodeRef> mlContainers = new ArrayList();
|
|
||||||
|
|
||||||
// construct the list of ML Container
|
|
||||||
for (ChildAssociationRef assoc : allChildAssoc)
|
|
||||||
{
|
|
||||||
NodeRef child = assoc.getChildRef();
|
|
||||||
|
|
||||||
QName type = directNodeService.getType(child);
|
|
||||||
|
|
||||||
if(type.equals(ContentModel.TYPE_CONTENT) &&
|
|
||||||
directNodeService.hasAspect(child, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT))
|
|
||||||
{
|
|
||||||
NodeRef container = multilingualContentService.getTranslationContainer(child);
|
|
||||||
|
|
||||||
if (!mlContainers.contains(container))
|
|
||||||
{
|
|
||||||
mlContainers.add(container);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// no specific treatment for folder and non-multilingual document
|
|
||||||
toReturn.add(assoc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// for each mlContainer found, choose the unique document to return
|
|
||||||
for(NodeRef container : mlContainers)
|
|
||||||
{
|
|
||||||
// get each translation language
|
|
||||||
Set<Locale> locales = multilingualContentService.getTranslations(container).keySet();
|
|
||||||
|
|
||||||
if(locales != null && locales.size() > 0)
|
|
||||||
{
|
|
||||||
Locale matchedLocal = I18NUtil.getNearestLocale(filterLocale, locales);
|
|
||||||
|
|
||||||
NodeRef matchedTranslation = null;
|
|
||||||
|
|
||||||
// if the filter language is not found
|
|
||||||
if(matchedLocal == null)
|
|
||||||
{
|
|
||||||
// get the pivot translation
|
|
||||||
matchedTranslation = multilingualContentService.getPivotTranslation(container);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// get the matched translation
|
|
||||||
matchedTranslation = multilingualContentService.getTranslations(container).get(matchedLocal);
|
|
||||||
}
|
|
||||||
|
|
||||||
toReturn.add(new ChildAssociationRef(null, null, null, matchedTranslation));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = toReturn;
|
|
||||||
|
|
||||||
if (logger.isDebugEnabled())
|
|
||||||
{
|
|
||||||
logger.debug("Filter has found " +
|
|
||||||
allChildAssoc.size() + " entries, " +
|
|
||||||
mlContainers.size() + " different ML Container " +
|
|
||||||
"and returns " + toReturn.size() + " nodes");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ret = invocation.proceed();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMultilingualContentService(
|
|
||||||
MultilingualContentService multilingualContentService)
|
|
||||||
{
|
|
||||||
this.multilingualContentService = multilingualContentService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDirectNodeService(NodeService nodeService)
|
|
||||||
{
|
|
||||||
this.directNodeService = nodeService;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -694,7 +694,6 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
// First get the node to ensure that it exists
|
// First get the node to ensure that it exists
|
||||||
Node node = getNodeNotNull(nodeRef);
|
Node node = getNodeNotNull(nodeRef);
|
||||||
|
|
||||||
boolean isArchivedNode = false;
|
|
||||||
boolean requiresDelete = false;
|
boolean requiresDelete = false;
|
||||||
|
|
||||||
// Invoke policy behaviours
|
// Invoke policy behaviours
|
||||||
@@ -713,7 +712,6 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
// the node has the temporary aspect meaning
|
// the node has the temporary aspect meaning
|
||||||
// it can not be archived
|
// it can not be archived
|
||||||
requiresDelete = true;
|
requiresDelete = true;
|
||||||
isArchivedNode = false;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -731,17 +729,15 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
{
|
{
|
||||||
// perform a normal deletion
|
// perform a normal deletion
|
||||||
nodeDaoService.deleteNode(node, true);
|
nodeDaoService.deleteNode(node, true);
|
||||||
isArchivedNode = false;
|
// Invoke policy behaviours
|
||||||
|
invokeOnDeleteNode(childAssocRef, nodeTypeQName, nodeAspectQNames, false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// archive it
|
// archive it
|
||||||
archiveNode(nodeRef, archiveStoreRef);
|
archiveNode(nodeRef, archiveStoreRef);
|
||||||
isArchivedNode = true;
|
// The archive performs a move, which will fire the appropriate OnDeleteNode
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoke policy behaviours
|
|
||||||
invokeOnDeleteNode(childAssocRef, nodeTypeQName, nodeAspectQNames, isArchivedNode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChildAssociationRef addChild(NodeRef parentRef, NodeRef childRef, QName assocTypeQName, QName assocQName)
|
public ChildAssociationRef addChild(NodeRef parentRef, NodeRef childRef, QName assocTypeQName, QName assocQName)
|
||||||
|
@@ -66,7 +66,7 @@ public class DbNodeServiceImplTest extends BaseNodeServiceTest
|
|||||||
|
|
||||||
protected NodeService getNodeService()
|
protected NodeService getNodeService()
|
||||||
{
|
{
|
||||||
return (NodeService) applicationContext.getBean("nodeService");
|
return (NodeService) applicationContext.getBean("dbNodeService");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -52,6 +52,15 @@ public interface MultilingualContentService
|
|||||||
@Auditable(key = Auditable.Key.ARG_0, parameters = {"localizedNodeRef"})
|
@Auditable(key = Auditable.Key.ARG_0, parameters = {"localizedNodeRef"})
|
||||||
void renameWithMLExtension(NodeRef localizedNodeRef);
|
void renameWithMLExtension(NodeRef localizedNodeRef);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether an existing document is part of a translation group.
|
||||||
|
*
|
||||||
|
* @param contentNodeRef An existing <b>cm:content</b>
|
||||||
|
* @return Returns <tt>true</tt> if the document has a <b>cm:mlContainer</b> parent
|
||||||
|
*/
|
||||||
|
@Auditable(key = Auditable.Key.ARG_0, parameters = {"contentNodeRef"})
|
||||||
|
boolean isTranslation(NodeRef contentNodeRef);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make an existing document into a translation by adding the <b>cm:mlDocument</b> aspect and
|
* Make an existing document into a translation by adding the <b>cm:mlDocument</b> aspect and
|
||||||
* creating a <b>cm:mlContainer</b> parent. If it is already a translation, then nothing is done.
|
* creating a <b>cm:mlContainer</b> parent. If it is already a translation, then nothing is done.
|
||||||
@@ -132,12 +141,14 @@ public interface MultilingualContentService
|
|||||||
List<Locale> getMissingTranslations(NodeRef localizedNodeRef, boolean addThisNodeLocale);
|
List<Locale> getMissingTranslations(NodeRef localizedNodeRef, boolean addThisNodeLocale);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given any node, this returns the pivot translation. The pivot translation is the translation
|
* Given any node, this returns the pivot translation. All multilingual documents belong to
|
||||||
* that its locale is referenced by the locale of the MLContainer. The translation can't be an
|
* a group linked by a hidden parent node of type <b>cm:mlContainer</b>. The pivot language
|
||||||
* empty translation.
|
* for the translations is stored on the parent, and the child that has the same locale is the
|
||||||
|
* pivot translation.
|
||||||
*
|
*
|
||||||
* @param nodeRef the node to test
|
* @param nodeRef a <b>cm:mlDocument</b>
|
||||||
* @return the pivot translation
|
* @return Returns a corresponding <b>cm:mlDocument</b> that matches the locale of
|
||||||
|
* of the <b>cm:mlContainer</b>.
|
||||||
*/
|
*/
|
||||||
@Auditable(key = Auditable.Key.ARG_0, parameters = {"nodeRef"})
|
@Auditable(key = Auditable.Key.ARG_0, parameters = {"nodeRef"})
|
||||||
NodeRef getPivotTranslation(NodeRef nodeRef);
|
NodeRef getPivotTranslation(NodeRef nodeRef);
|
||||||
|
@@ -26,6 +26,7 @@ package org.alfresco.service.cmr.model;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.alfresco.service.cmr.repository.ContentData;
|
import org.alfresco.service.cmr.repository.ContentData;
|
||||||
@@ -61,6 +62,15 @@ public interface FileInfo
|
|||||||
*/
|
*/
|
||||||
public NodeRef getLinkNodeRef();
|
public NodeRef getLinkNodeRef();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all translated versions of the file info. The map will always be empty if this
|
||||||
|
* instance references {@link #isFolder() a folder}. The map may also be empty if the
|
||||||
|
* file represented is not multilingual.
|
||||||
|
*
|
||||||
|
* @return Returns a map of transalations keyed on locale
|
||||||
|
*/
|
||||||
|
public Map<Locale, FileInfo> getTranslations();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Returns the name of the file or folder within the parent folder
|
* @return Returns the name of the file or folder within the parent folder
|
||||||
*/
|
*/
|
||||||
|
Reference in New Issue
Block a user