From fcddd9a468fe664c9c624c1607f957dff6ad73db Mon Sep 17 00:00:00 2001 From: Derek Hulley Date: Thu, 1 Feb 2007 16:11:40 +0000 Subject: [PATCH] CIRCA Multilingual changes (Philippe Dubois) - Web Client support for changing content filter language - I18NUtil support for contentLocale - MLPropertyInterceptor handling of properties inbound and outbound TODO: - Is new Locale("") valid? - Some more tests to ensure property interceptor is working - Move interceptor into .sample config file git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@5003 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/node-services-context.xml | 9 +- .../ml/MultilingualContentServiceImpl.java | 11 +- .../MultilingualContentServiceImplTest.java | 4 +- .../repo/node/MLPropertyInterceptor.java | 168 +++++++++++++++++- .../cmr/ml/MultilingualContentService.java | 12 +- 5 files changed, 189 insertions(+), 15 deletions(-) diff --git a/config/alfresco/node-services-context.xml b/config/alfresco/node-services-context.xml index b85af1fa11..cbac88e91b 100644 --- a/config/alfresco/node-services-context.xml +++ b/config/alfresco/node-services-context.xml @@ -4,7 +4,14 @@ - + + + + + + + + diff --git a/source/java/org/alfresco/repo/model/ml/MultilingualContentServiceImpl.java b/source/java/org/alfresco/repo/model/ml/MultilingualContentServiceImpl.java index d4deede7bb..ad81a1a605 100644 --- a/source/java/org/alfresco/repo/model/ml/MultilingualContentServiceImpl.java +++ b/source/java/org/alfresco/repo/model/ml/MultilingualContentServiceImpl.java @@ -46,6 +46,7 @@ import org.apache.commons.logging.LogFactory; * Multilingual support implementation * * @author Derek Hulley + * @author Philippe Dubois */ public class MultilingualContentServiceImpl implements MultilingualContentService { @@ -216,6 +217,13 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic // The aspect is present, so just ensure that the locale is correct nodeService.setProperty(contentNodeRef, ContentModel.PROP_LOCALE, locale); } + //get all the languages already there + Map existingLanguages = this.getTranslations(mlContainerNodeRef); + if (existingLanguages.containsKey(locale)) + { + throw new AlfrescoRuntimeException("Duplicate locale in document pool:" + locale.toString()); + } + // Do we make use of an existing container? if (mlContainerNodeRef == null) { @@ -288,8 +296,9 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic } /** @inheritDoc */ - public void createEdition(NodeRef mlContainerNodeRef, NodeRef translationNodeRef) + public void createEdition( NodeRef translationNodeRef) { + NodeRef mlContainerNodeRef = getOrCreateMLContainer(translationNodeRef, false); // Ensure that the translation given is one of the children getOrCreateMLContainer(translationNodeRef, false); // Get all the container's children diff --git a/source/java/org/alfresco/repo/model/ml/MultilingualContentServiceImplTest.java b/source/java/org/alfresco/repo/model/ml/MultilingualContentServiceImplTest.java index 861d04f24c..0310f60708 100644 --- a/source/java/org/alfresco/repo/model/ml/MultilingualContentServiceImplTest.java +++ b/source/java/org/alfresco/repo/model/ml/MultilingualContentServiceImplTest.java @@ -31,7 +31,6 @@ import org.alfresco.repo.transaction.TransactionUtil.TransactionWork; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.ml.MultilingualContentService; import org.alfresco.service.cmr.model.FileFolderService; -import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; @@ -49,6 +48,7 @@ import org.springframework.context.ApplicationContext; * @see org.alfresco.repo.ml.MultilingualContentServiceImpl * * @author Derek Hulley + * @author Philippe Dubois */ public class MultilingualContentServiceImplTest extends TestCase { @@ -203,7 +203,7 @@ public class MultilingualContentServiceImplTest extends TestCase Version japaneseVersionPreEdition = versionService.getCurrentVersion(japaneseContentNodeRef); // Create the edition, keeping the Chinese translation as the basis - multilingualContentService.createEdition(mlContainerNodeRef, chineseContentNodeRef); + multilingualContentService.createEdition(chineseContentNodeRef); // Check the container child count assertEquals("Incorrect number of child nodes", 1, nodeService.getChildAssocs(mlContainerNodeRef).size()); diff --git a/source/java/org/alfresco/repo/node/MLPropertyInterceptor.java b/source/java/org/alfresco/repo/node/MLPropertyInterceptor.java index bf34ea6b1e..817cf0e53c 100644 --- a/source/java/org/alfresco/repo/node/MLPropertyInterceptor.java +++ b/source/java/org/alfresco/repo/node/MLPropertyInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Alfresco, Inc. + * Copyright (C) 2007 Alfresco, Inc. * * Licensed under the Mozilla Public License version 1.1 * with a permitted attribution clause. You may obtain a @@ -18,10 +18,16 @@ package org.alfresco.repo.node; import java.io.Serializable; import java.util.HashMap; +import java.util.Locale; import java.util.Map; +import org.alfresco.i18n.I18NUtil; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.repository.MLText; 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; @@ -46,15 +52,73 @@ import org.apache.commons.logging.LogFactory; * @see org.alfresco.service.cmr.repository.NodeService#setProperties(NodeRef, Map) * * @author Derek Hulley + * @author Philippe Dubois */ public class MLPropertyInterceptor implements MethodInterceptor { private static Log logger = LogFactory.getLog(MLPropertyInterceptor.class); + + private static ThreadLocal mlAware = new ThreadLocal(); + + /** Direct access to the NodeService */ + private NodeService directNodeService; + /** Used to access property definitions */ + private DictionaryService dictionaryService; + + /** + * Change the filtering behaviour of this interceptor on the curren thread. + * Use this to switch off the filtering and just pass out properties as + * handed out of the node service. + * + * @param mlAwareVal true if the current thread is able to handle + * {@link MLText d:mltext} property types, otherwise false. + */ + static public void setMLAware(boolean mlAwareVal) + { + mlAware.set(new Boolean(mlAwareVal)); + } + + /** + * @return Returns true if the current thread has marked itself + * as being able to handle {@link MLText d:mltext} types properly. + */ + static public boolean isMLAware() + { + if (mlAware.get() == null) + { + return false; + } + else + { + return mlAware.get(); + } + } + + public void setDirectNodeService(NodeService bean) + { + this.directNodeService = bean; + } + + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + @SuppressWarnings("unchecked") public Object invoke(MethodInvocation invocation) throws Throwable { + Locale contentLocale = I18NUtil.getContentLocale(); Object ret = null; + + // If isMLAware then no treatment is done, just return + if(isMLAware()) + { + // Don't interfere + return invocation.proceed(); + } + String methodName = invocation.getMethod().getName(); if (methodName.equals("getProperty")) { @@ -62,8 +126,8 @@ public class MLPropertyInterceptor implements MethodInterceptor // The return value might need to be converted to a String if (ret != null && ret instanceof MLText) { - MLText mlText = (MLText) ret; - ret = mlText.getDefaultValue(); + MLText mlText = (MLText) ret; + ret = mlText.getClosestValue(contentLocale); // done if (logger.isDebugEnabled()) { @@ -86,7 +150,7 @@ public class MLPropertyInterceptor implements MethodInterceptor if (value != null && value instanceof MLText) { MLText mlText = (MLText) value; - value = mlText.getDefaultValue(); + value = mlText.getClosestValue(contentLocale); // Store the converted value convertedProperties.put(key, value); } @@ -106,6 +170,102 @@ public class MLPropertyInterceptor implements MethodInterceptor " converted: " + convertedProperties); } } + else if (methodName.equals("setProperties")) + { + //get the raw properties and for every multi lingual property, + //take the value transmited as parameter and if type string put the value in + //the multilingual property. If not just transfer property value + NodeRef node = (NodeRef)invocation.getArguments()[0]; + //new properties + Map newProperties =(Map)(invocation.getArguments()[1]); + //get the raw property values + Map previousProperties = directNodeService.getProperties(node); + //merge with previous properties only for MLText ones + Map convertedProperties = new HashMap(newProperties.size() * 2); + for (Map.Entry entry : newProperties.entrySet()) + { + QName key = entry.getKey(); + PropertyDefinition propertyDef = this.dictionaryService.getProperty(key); + if (propertyDef == null) + { + //no type found in the model, just transfer + convertedProperties.put(key,newProperties.get(key)); + continue; + } + //if incoming new property is MLText(based on the model) then + //transfer or merge depending on the incoming concrete type. + //if incoming type is String and model indicates MLText then merge if something else just transfer + if(propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT) && + newProperties.get(key) instanceof String ) + { + //get back previous value it should a MLText or null + + Serializable preVal = previousProperties.get(key); + MLText newVal = new MLText(); + if (preVal == null || !(preVal instanceof MLText)) + { + //second test is in case of the value was set before in the system without MLText + //create a new MLText and transfer + //if prevval wasn't null save value with as LOCAL.ENGLISH (purely arbitrary) to avoid any + //information loss + if(preVal!= null) + newVal.addValue(Locale.ENGLISH,(String)preVal); + } + else + { + newVal = (MLText)preVal; + } + //use alternate locale + newVal.addValue( + contentLocale, + (String)newProperties.get(key)); + //transfer + convertedProperties.put(key,newVal); + continue; + } + //normal process, just transfer + convertedProperties.put(key,newProperties.get(key)); + }//for + directNodeService.setProperties(node, convertedProperties); + } + else if (methodName.equals("setProperty")) + { + //check if the property is of type MLText + QName qPropName = (QName)invocation.getArguments()[1]; + PropertyDefinition propertyDef = this.dictionaryService.getProperty(qPropName); + //if no type definition associated to the name then just proceed + if (propertyDef != null && propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT)) + { + + //check if property is multi valued or actual set value is MLText + // in that case delegate deletate + if (propertyDef.isMultiValued() || (invocation.getArguments()[2] instanceof MLText) ) + { + ret = invocation.proceed(); + } + else + { + //this is a multilingual mono valued property + //then get the MLText value and set the linguistic value conrresponding + //of the preferate language + NodeRef node = (NodeRef)invocation.getArguments()[0]; + MLText mlPropertyValue = (MLText)directNodeService.getProperty(node, qPropName); + if (mlPropertyValue == null) + { + mlPropertyValue = new MLText(); + } + mlPropertyValue.addValue( + contentLocale, + (String) invocation.getArguments()[2]); + //set the value following according to the current locale + directNodeService.setProperty(node, qPropName, mlPropertyValue); + } + } + else + { + ret = invocation.proceed(); + } + } else { ret = invocation.proceed(); diff --git a/source/java/org/alfresco/service/cmr/ml/MultilingualContentService.java b/source/java/org/alfresco/service/cmr/ml/MultilingualContentService.java index 28462f2d70..f0ea039bc2 100644 --- a/source/java/org/alfresco/service/cmr/ml/MultilingualContentService.java +++ b/source/java/org/alfresco/service/cmr/ml/MultilingualContentService.java @@ -16,21 +16,19 @@ */ package org.alfresco.service.cmr.ml; -import java.util.Collection; -import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import org.alfresco.service.Auditable; import org.alfresco.service.PublicService; -import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; /** * The API to manage multilingual content and related structures. * * @author Derek Hulley + * @author Philippe Dubois */ @PublicService public interface MultilingualContentService @@ -76,14 +74,14 @@ public interface MultilingualContentService NodeRef getTranslationContainer(NodeRef translationNodeRef); /** - * Create a new edition of an existing cm:mlContainer. + * Create a new edition of an existing cm:mlContainer using any one of the + * associated cm:mlDocument transalations. * - * @param mlContainerNodeRef An existing cm:mlContainer * @param translationNodeRef The specific cm:mlDocument to use as the starting point * of the new edition. All other translations will be removed. */ - @Auditable(key = Auditable.Key.ARG_0, parameters = {"mlContainerNodeRef", "translationNodeRef"}) - void createEdition(NodeRef mlContainerNodeRef, NodeRef translationNodeRef); + @Auditable(key = Auditable.Key.ARG_0, parameters = {"translationNodeRef"}) + void createEdition(NodeRef translationNodeRef); /** * Gets the set of sibling translations associated with the given cm:mlDocument or