Checkpoint of MultilingualContentService

- New patch for multilingual root for holding containers


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@4730 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2007-01-04 17:14:51 +00:00
parent 7e4430cae9
commit 96653f2420
12 changed files with 504 additions and 29 deletions

View File

@@ -43,7 +43,7 @@ public interface ContentModel
// tag for temporary nodes
static final QName ASPECT_TEMPORARY = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "temporary");
// tag for temporary nodes
// tag for localized nodes
static final QName ASPECT_LOCALIZED = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "localized");
static final QName PROP_LOCALE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "locale");
@@ -200,6 +200,10 @@ public interface ContentModel
static final QName ASPECT_REFERENCES_NODE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "referencesnode");
static final QName PROP_NODE_REF = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "noderef");
// Multilingual Type
static final QName TYPE_MULTILINGUAL_CONTAINER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "mlContainer");
static final QName ASSOC_MULTILINGUAL_CHILD = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "mlChild");
static final QName ASPECT_MULTILINGUAL_DOCUMENT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "mlDocument");
//
// User Model Definitions

View File

@@ -0,0 +1,226 @@
/*
* Copyright (C) 2007 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.model.ml;
import java.util.List;
import java.util.Locale;
import org.alfresco.error.AlfrescoRuntimeException;
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.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.version.VersionService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.util.PropertyMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Multilingual support implementation
*
* @author Derek Hulley
*/
public class MultilingualContentServiceImpl implements MultilingualContentService
{
private static Log logger = LogFactory.getLog(MultilingualContentServiceImpl.class);
private NodeService nodeService;
private SearchService searchService;
private VersionService versionService;
private SearchParameters searchParametersMLRoot;
public MultilingualContentServiceImpl()
{
searchParametersMLRoot = new SearchParameters();
searchParametersMLRoot.setLanguage(SearchService.LANGUAGE_XPATH);
searchParametersMLRoot.setLimit(1);
searchParametersMLRoot.addStore(new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore"));
searchParametersMLRoot.setQuery("/cm:multilingualRoot");
}
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public void setSearchService(SearchService searchService)
{
this.searchService = searchService;
}
public void setVersionService(VersionService versionService)
{
this.versionService = versionService;
}
public void renameWithMLExtension(NodeRef translationNodeRef)
{
throw new UnsupportedOperationException();
}
/**
* @return Returns a reference to the node that will hold all the <b>cm:mlContainer</b> nodes.
*/
private NodeRef getMLContainerRoot()
{
ResultSet rs = searchService.query(searchParametersMLRoot);
try
{
if (rs.length() > 0)
{
NodeRef mlRootNodeRef = rs.getNodeRef(0);
// done
return mlRootNodeRef;
}
else
{
throw new AlfrescoRuntimeException(
"Unable to find bootstrap location for ML Root using query: " + searchParametersMLRoot.getQuery());
}
}
finally
{
rs.close();
}
}
private static final QName QNAME_ML_CONTAINER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "mlContainer");
private static final QName QNAME_ML_TRANSLATION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "mlTranslation");
/**
* @return Returns a new <b>cm:mlContainer</b>
*/
private NodeRef makeMLContainer()
{
NodeRef mlContainerRootNodeRef = getMLContainerRoot();
// Create the container
ChildAssociationRef assocRef = nodeService.createNode(
mlContainerRootNodeRef,
ContentModel.ASSOC_CHILDREN,
QNAME_ML_CONTAINER,
ContentModel.TYPE_MULTILINGUAL_CONTAINER);
// done
return assocRef.getChildRef();
}
/**
* Retrieve or create a <b>cm:mlDocument</b> container for the given node, which must have the
* <b>cm:mlDocument</b> already applied.
*
* @param mlDocumentNodeRef an existing <b>cm:mlDocument</b>
* @return Returns the <b>cm:mlContainer</b> parent
*/
private NodeRef getOrCreateMLContainer(NodeRef mlDocumentNodeRef)
{
if (!nodeService.hasAspect(mlDocumentNodeRef, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT))
{
throw new IllegalArgumentException(
"Node must have aspect " + ContentModel.ASPECT_MULTILINGUAL_DOCUMENT + " applied");
}
// Now check if a parent mlContainer exists
NodeRef mlContainerNodeRef = null;
boolean createAssociation = false;
List<ChildAssociationRef> parentAssocRefs = nodeService.getParentAssocs(
mlDocumentNodeRef,
ContentModel.ASSOC_MULTILINGUAL_CHILD,
RegexQNamePattern.MATCH_ALL);
if (parentAssocRefs.size() == 0)
{
// Create a ML container
mlContainerNodeRef = makeMLContainer();
createAssociation = true;
}
else if (parentAssocRefs.size() == 1)
{
// Just get it
ChildAssociationRef toKeepAssocRef = parentAssocRefs.get(0);
mlContainerNodeRef = toKeepAssocRef.getParentRef();
createAssociation = true;
}
else if (parentAssocRefs.size() > 1)
{
// This is a problem - destroy all but the first
logger.warn("Cleaning up multiple multilingual containers on node: " + mlDocumentNodeRef);
ChildAssociationRef toKeepAssocRef = parentAssocRefs.get(0);
mlContainerNodeRef = toKeepAssocRef.getParentRef();
// Remove all the associations to the container
boolean first = true;
for (ChildAssociationRef assocRef : parentAssocRefs)
{
if (first)
{
first = false;
continue;
}
nodeService.removeChildAssociation(assocRef);
}
}
// Associate the translation with the container
if (createAssociation)
{
nodeService.addChild(
mlContainerNodeRef,
mlDocumentNodeRef,
ContentModel.ASSOC_MULTILINGUAL_CHILD,
QNAME_ML_TRANSLATION);
}
// done
return mlContainerNodeRef;
}
public NodeRef makeTranslation(NodeRef contentNodeRef, Locale locale)
{
// Add the aspect using the given locale, of necessary
if (!nodeService.hasAspect(contentNodeRef, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT))
{
PropertyMap properties = new PropertyMap();
properties.put(ContentModel.PROP_LOCALE, locale);
nodeService.addAspect(contentNodeRef, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT, properties);
}
else
{
// The aspect is present, so just ensure that the locale is correct
nodeService.setProperty(contentNodeRef, ContentModel.PROP_LOCALE, locale);
}
// Get or create the container
NodeRef mlContainerNodeRef = getOrCreateMLContainer(contentNodeRef);
// done
return mlContainerNodeRef;
}
public NodeRef addTranslation(NodeRef newTranslationNodeRef, NodeRef translationOfNodeRef, Locale locale)
{
throw new UnsupportedOperationException();
}
public NodeRef getTranslationContainer(NodeRef translationNodeRef)
{
throw new UnsupportedOperationException();
}
public NodeRef createEdition(NodeRef mlContainerNodeRef, NodeRef translationNodeRef)
{
throw new UnsupportedOperationException();
}
}

View File

@@ -0,0 +1,131 @@
/*
* Copyright (C) 2007 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.model.ml;
import java.util.Locale;
import junit.framework.TestCase;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.transaction.TransactionUtil;
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.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper;
import org.springframework.context.ApplicationContext;
/**
* @see org.alfresco.repo.ml.MultilingualContentServiceImpl
*
* @author Derek Hulley
*/
public class MultilingualContentServiceImplTest extends TestCase
{
private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
private ServiceRegistry serviceRegistry;
private AuthenticationComponent authenticationComponent;
private TransactionService transactionService;
private NodeService nodeService;
private FileFolderService fileFolderService;
private MultilingualContentService multilingualContentService;
private NodeRef folderNodeRef;
@Override
protected void setUp() throws Exception
{
serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY);
authenticationComponent = (AuthenticationComponent) ctx.getBean("AuthenticationComponent");
transactionService = serviceRegistry.getTransactionService();
nodeService = serviceRegistry.getNodeService();
fileFolderService = serviceRegistry.getFileFolderService();
multilingualContentService = (MultilingualContentService) ctx.getBean("MultilingualContentService");
// Run as admin
authenticationComponent.setCurrentUser("admin");
// Create a folder to work in
TransactionWork<NodeRef> createFolderWork = new TransactionWork<NodeRef>()
{
public NodeRef doWork() throws Exception
{
StoreRef storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore");
NodeRef rootNodeRef = nodeService.getRootNode(storeRef);
// Create the folder
NodeRef folderNodeRef = nodeService.createNode(
rootNodeRef,
ContentModel.ASSOC_CHILDREN,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "testFolder"),
ContentModel.TYPE_FOLDER).getChildRef();
// done
return folderNodeRef;
}
};
folderNodeRef = TransactionUtil.executeInUserTransaction(transactionService, createFolderWork);
}
@Override
protected void tearDown() throws Exception
{
// Clear authentication
try
{
authenticationComponent.clearCurrentSecurityContext();
}
catch (Throwable e)
{
e.printStackTrace();
}
}
private NodeRef createContent()
{
NodeRef contentNodeRef = fileFolderService.create(
folderNodeRef,
"" + System.currentTimeMillis(),
ContentModel.TYPE_CONTENT).getNodeRef();
// add some content
ContentWriter contentWriter = fileFolderService.getWriter(contentNodeRef);
contentWriter.putContent("ABC");
// done
return contentNodeRef;
}
public void testSetup() throws Exception
{
// Ensure that content can be created
createContent();
}
public void testMakeTranslation() throws Exception
{
NodeRef contentNodeRef = createContent();
// Turn the content into a translation with the appropriate structures
NodeRef mlContainerNodeRef = multilingualContentService.makeTranslation(contentNodeRef, Locale.CHINESE);
// Check it
assertNotNull("Container not created", mlContainerNodeRef);
}
}

View File

@@ -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
@@ -31,38 +31,41 @@ import org.alfresco.service.cmr.repository.NodeRef;
public interface MultilingualContentService
{
/**
* Rename an existing <b>cm:translation</b> by adding locale suffixes to the base name.
* Rename an existing <b>sys:localized</b> by adding locale suffixes to the base name.
* Where there are name clashes with existing documents, a numerical naming scheme will be
* adopted.
*
* @param translationNodeRef An existing <b>cm:translation</b>
* @param localizedNodeRef An existing <b>sys:localized</b>
*/
@Auditable(key = Auditable.Key.ARG_0, parameters = {"translationNodeRef"})
void renameWithMLExtension(NodeRef translationNodeRef);
@Auditable(key = Auditable.Key.ARG_0, parameters = {"localizedNodeRef"})
void renameWithMLExtension(NodeRef localizedNodeRef);
/**
* Make an existing document translatable. If it is already translatable, then nothing is done.
* 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.
*
* @param contentNodeRef An existing <b>cm:content</b>
* @return Returns the <b>cm:mlContainer</b> translation parent
* @param contentNodeRef An existing <b>cm:content</b>
* @return Returns the <b>cm:mlContainer</b> translation parent
*
* @see org.alfresco.model.ContentModel#ASPECT_MULTILINGUAL_DOCUMENT
*/
@Auditable(key = Auditable.Key.ARG_0, parameters = {"contentNodeRef", "locale"})
NodeRef makeTranslatable(NodeRef contentNodeRef, Locale locale);
NodeRef makeTranslation(NodeRef contentNodeRef, Locale locale);
/**
* Make a translation out of an existing document. The necessary translation structures will be created
* as necessary.
*
* @param newTranslationNodeRef An existing <b>cm:content</b>
* @param translationOfNodeRef An existing <b>cm:translation</b> or <b>cm:mlContainer</b>
* @return Returns the <b>cm:mlContainer</b> translation parent
* @param newTranslationNodeRef An existing <b>cm:content</b>
* @param translationOfNodeRef An existing <b>cm:mlDocument</b> or <b>cm:mlContainer</b>
* @return Returns the <b>cm:mlContainer</b> translation parent
*/
@Auditable(key = Auditable.Key.ARG_0, parameters = {"newTranslationNodeRef", "translationOfNodeRef", "locale"})
NodeRef addTranslation(NodeRef newTranslationNodeRef, NodeRef translationOfNodeRef, Locale locale);
/**
*
* @return Returns the <b>cm:mlContainer</b> translation parent
* @return Returns the <b>cm:mlContainer</b> translation parent
*/
@Auditable(key = Auditable.Key.ARG_0, parameters = {"translationNodeRef"})
NodeRef getTranslationContainer(NodeRef translationNodeRef);
@@ -70,10 +73,10 @@ public interface MultilingualContentService
/**
* Create a new edition of an existing <b>cm:mlContainer</b>.
*
* @param mlContainerNodeRef An existing <b>cm:mlContainer</b>
* @param translationNodeRef The specific <b>cm:translation</b> to use as the starting point
* of the new edition.
* @return Returns the <b>cm:mlContainer</b>
* @param mlContainerNodeRef An existing <b>cm:mlContainer</b>
* @param translationNodeRef The specific <b>cm:mlDocument</b> to use as the starting point
* of the new edition.
* @return Returns the <b>cm:mlContainer</b>
*/
@Auditable(key = Auditable.Key.ARG_0, parameters = {"mlContainerNodeRef", "translationNodeRef"})
NodeRef createEdition(NodeRef mlContainerNodeRef, NodeRef translationNodeRef);