mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-10-08 14:51:49 +00:00
125788 rmunteanu: Merged 5.1.N (5.1.2) to 5.2.N (5.2.1) 125606 rmunteanu: Merged 5.1.1 (5.1.1) to 5.1.N (5.1.2) 125515 slanglois: MNT-16155 Update source headers - add new Copyrights for Java and JSP source files + automatic check in the build git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@127810 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
1266 lines
47 KiB
Java
1266 lines
47 KiB
Java
/*
|
|
* #%L
|
|
* Alfresco Repository
|
|
* %%
|
|
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
|
* %%
|
|
* This file is part of the Alfresco software.
|
|
* If the software was purchased under a paid Alfresco license, the terms of
|
|
* the paid license agreement will prevail. Otherwise, the software is
|
|
* provided under the following open source license terms:
|
|
*
|
|
* Alfresco is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Alfresco is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
|
* #L%
|
|
*/
|
|
|
|
package org.alfresco.repo.dictionary;
|
|
|
|
import java.io.ByteArrayInputStream;
|
|
import java.io.ByteArrayOutputStream;
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.Serializable;
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
import javax.xml.xpath.XPath;
|
|
import javax.xml.xpath.XPathConstants;
|
|
import javax.xml.xpath.XPathExpression;
|
|
import javax.xml.xpath.XPathFactory;
|
|
|
|
import org.alfresco.error.AlfrescoRuntimeException;
|
|
import org.alfresco.model.ContentModel;
|
|
import org.alfresco.query.EmptyPagingResults;
|
|
import org.alfresco.query.PageDetails;
|
|
import org.alfresco.query.PagingRequest;
|
|
import org.alfresco.query.PagingResults;
|
|
import org.alfresco.repo.admin.RepoAdminServiceImpl;
|
|
import org.alfresco.repo.content.MimetypeMap;
|
|
import org.alfresco.repo.download.DownloadModel;
|
|
import org.alfresco.repo.download.DownloadStorage;
|
|
import org.alfresco.repo.model.filefolder.HiddenAspect;
|
|
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
|
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
|
import org.alfresco.service.cmr.admin.RepoAdminService;
|
|
import org.alfresco.service.cmr.dictionary.AspectDefinition;
|
|
import org.alfresco.service.cmr.dictionary.ClassDefinition;
|
|
import org.alfresco.service.cmr.dictionary.Constraint;
|
|
import org.alfresco.service.cmr.dictionary.ConstraintDefinition;
|
|
import org.alfresco.service.cmr.dictionary.CustomModelDefinition;
|
|
import org.alfresco.service.cmr.dictionary.CustomModelException;
|
|
import org.alfresco.service.cmr.dictionary.CustomModelService;
|
|
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
|
import org.alfresco.service.cmr.dictionary.ModelDefinition;
|
|
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
|
import org.alfresco.service.cmr.dictionary.TypeDefinition;
|
|
import org.alfresco.service.cmr.dictionary.DictionaryException.DuplicateDefinitionException;
|
|
import org.alfresco.service.cmr.download.DownloadService;
|
|
import org.alfresco.service.cmr.repository.ContentReader;
|
|
import org.alfresco.service.cmr.repository.ContentService;
|
|
import org.alfresco.service.cmr.repository.ContentWriter;
|
|
import org.alfresco.service.cmr.repository.NodeRef;
|
|
import org.alfresco.service.cmr.repository.NodeService;
|
|
import org.alfresco.service.cmr.repository.StoreRef;
|
|
import org.alfresco.service.cmr.search.SearchService;
|
|
import org.alfresco.service.cmr.security.AuthorityService;
|
|
import org.alfresco.service.cmr.security.PermissionService;
|
|
import org.alfresco.service.namespace.QName;
|
|
import org.alfresco.util.Pair;
|
|
import org.alfresco.util.ParameterCheck;
|
|
import org.alfresco.util.PropertyCheck;
|
|
import org.alfresco.util.TempFileProvider;
|
|
import org.alfresco.util.XMLUtil;
|
|
import org.apache.commons.logging.Log;
|
|
import org.apache.commons.logging.LogFactory;
|
|
import org.w3c.dom.Document;
|
|
import org.w3c.dom.Node;
|
|
|
|
/**
|
|
* Custom Model Service Implementation
|
|
*
|
|
* @author Jamal Kaabi-Mofrad
|
|
*/
|
|
public class CustomModelServiceImpl implements CustomModelService
|
|
{
|
|
private static final Log logger = LogFactory.getLog(CustomModelServiceImpl.class);
|
|
|
|
public static final String DEFAULT_CUSTOM_MODEL_ASPECT = "hasAspect('cmm:customModelManagement')";
|
|
|
|
public static final QName ASPECT_CUSTOM_MODEL = QName.createQName("http://www.alfresco.org/model/custommodelmanagement/1.0", "customModelManagement");
|
|
|
|
public static final String ALFRESCO_MODEL_ADMINISTRATORS_AUTHORITY = "ALFRESCO_MODEL_ADMINISTRATORS";
|
|
public static final String GROUP_ALFRESCO_MODEL_ADMINISTRATORS_AUTHORITY = PermissionService.GROUP_PREFIX
|
|
+ ALFRESCO_MODEL_ADMINISTRATORS_AUTHORITY;
|
|
|
|
public static final String SHARE_EXT_MODULE_SUFFIX = "_module.xml";
|
|
|
|
/** Messages */
|
|
private static final String MSG_NAME_ALREADY_IN_USE = "cmm.service.name_already_in_use";
|
|
private static final String MSG_CREATE_MODEL_ERR = "cmm.service.create_model_err";
|
|
private static final String MSG_UPDATE_MODEL_ERR = "cmm.service.update_model_err";
|
|
private static final String MSG_MULTIPLE_MODELS = "cmm.service.multiple_models";
|
|
private static final String MSG_RETRIEVE_MODEL = "cmm.service.retrieve_model";
|
|
private static final String MSG_MODEL_NOT_EXISTS = "cmm.service.model_not_exists";
|
|
private static final String MSG_NAMESPACE_NOT_EXISTS = "cmm.service.namespace_not_exists";
|
|
private static final String MSG_NAMESPACE_MANY_EXIST = "cmm.service.namespace_many_exist";
|
|
private static final String MSG_NAMESPACE_URI_ALREADY_IN_USE = "cmm.service.namespace_uri_already_in_use";
|
|
private static final String MSG_NAMESPACE_PREFIX_ALREADY_IN_USE = "cmm.service.namespace_prefix_already_in_use";
|
|
private static final String MSG_UNABLE_DELETE_ACTIVE_MODEL = "cmm.service.unable_delete_active_model";
|
|
private static final String MSG_UNABLE_MODEL_DELETE = "cmm.service.unable_model_delete";
|
|
private static final String MSG_UNABLE_MODEL_DEACTIVATE = "cmm.service.unable_model_deactivate";
|
|
private static final String MSG_UNABLE_MODEL_ACTIVATE = "cmm.service.unable_model_activate";
|
|
private static final String MSG_INVALID_MODEL = "cmm.service.invalid_model";
|
|
private static final String MSG_NAMESPACE_ACTIVE_MODEL = "cmm.service.namespace_active_model";
|
|
private static final String MSG_FAILED_DEACTIVATION_TYPE_DEPENDENCY = "cmm.service.failed.deactivation.type.dependency";
|
|
private static final String MSG_FAILED_DEACTIVATION_ASPECT_DEPENDENCY = "cmm.service.failed.deactivation.aspect.dependency";
|
|
private static final String MSG_DOWNLOAD_COPY_MODEL_ERR = "cmm.service.download.create_model_copy_err";
|
|
private static final String MSG_DOWNLOAD_CREATE_SHARE_EXT_ERR = "cmm.service.download.create_share_ext_err";
|
|
|
|
private NodeService nodeService;
|
|
private DictionaryDAOImpl dictionaryDAO;
|
|
private ContentService contentService;
|
|
private SearchService searchService;
|
|
private RepositoryLocation repoModelsLocation;
|
|
private NamespaceDAO namespaceDAO;
|
|
private DictionaryService dictionaryService;
|
|
private RetryingTransactionHelper retryingTransactionHelper;
|
|
private RepoAdminService repoAdminService;
|
|
private AuthorityService authorityService;
|
|
private HiddenAspect hiddenAspect;
|
|
private DownloadService downloadService;
|
|
private DownloadStorage downloadStorage;
|
|
|
|
private String shareExtModulePath;
|
|
|
|
|
|
public void setNodeService(NodeService nodeService)
|
|
{
|
|
this.nodeService = nodeService;
|
|
}
|
|
|
|
public void setDictionaryDAO(DictionaryDAOImpl dictionaryDAO)
|
|
{
|
|
this.dictionaryDAO = dictionaryDAO;
|
|
}
|
|
|
|
public void setContentService(ContentService contentService)
|
|
{
|
|
this.contentService = contentService;
|
|
}
|
|
|
|
public void setSearchService(SearchService searchService)
|
|
{
|
|
this.searchService = searchService;
|
|
}
|
|
|
|
public void setRepositoryModelsLocation(RepositoryLocation repoModelsLocation)
|
|
{
|
|
this.repoModelsLocation = repoModelsLocation;
|
|
}
|
|
|
|
public void setDictionaryService(DictionaryService dictionaryService)
|
|
{
|
|
this.dictionaryService = dictionaryService;
|
|
}
|
|
|
|
public void setNamespaceDAO(NamespaceDAO namespaceDAO)
|
|
{
|
|
this.namespaceDAO = namespaceDAO;
|
|
}
|
|
|
|
public void setRetryingTransactionHelper(RetryingTransactionHelper retryingTransactionHelper)
|
|
{
|
|
this.retryingTransactionHelper = retryingTransactionHelper;
|
|
}
|
|
|
|
public void setRepoAdminService(RepoAdminService repoAdminService)
|
|
{
|
|
this.repoAdminService = repoAdminService;
|
|
}
|
|
|
|
public void setAuthorityService(AuthorityService authorityService)
|
|
{
|
|
this.authorityService = authorityService;
|
|
}
|
|
|
|
public void setHiddenAspect(HiddenAspect hiddenAspect)
|
|
{
|
|
this.hiddenAspect = hiddenAspect;
|
|
}
|
|
|
|
public void setDownloadService(DownloadService downloadSerivce)
|
|
{
|
|
this.downloadService = downloadSerivce;
|
|
}
|
|
|
|
public void setDownloadStorage(DownloadStorage downloadStorage)
|
|
{
|
|
this.downloadStorage = downloadStorage;
|
|
}
|
|
|
|
public void setShareExtModulePath(String shareExtModulePath)
|
|
{
|
|
this.shareExtModulePath = shareExtModulePath;
|
|
}
|
|
|
|
/**
|
|
* Checks that all necessary properties and services have been provided.
|
|
*/
|
|
public void init()
|
|
{
|
|
PropertyCheck.mandatory(this, "nodeService", nodeService);
|
|
PropertyCheck.mandatory(this, "dictionaryDAO", dictionaryDAO);
|
|
PropertyCheck.mandatory(this, "contentService", contentService);
|
|
PropertyCheck.mandatory(this, "searchService", searchService);
|
|
PropertyCheck.mandatory(this, "repoModelsLocation", repoModelsLocation);
|
|
PropertyCheck.mandatory(this, "namespaceDAO", namespaceDAO);
|
|
PropertyCheck.mandatory(this, "dictionaryService", dictionaryService);
|
|
PropertyCheck.mandatory(this, "retryingTransactionHelper", retryingTransactionHelper);
|
|
PropertyCheck.mandatory(this, "repoAdminService", repoAdminService);
|
|
PropertyCheck.mandatory(this, "authorityService", authorityService);
|
|
PropertyCheck.mandatory(this, "hiddenAspect", hiddenAspect);
|
|
PropertyCheck.mandatory(this, "shareExtModulePath", shareExtModulePath);
|
|
PropertyCheck.mandatory(this, "downloadService", downloadService);
|
|
PropertyCheck.mandatory(this, "downloadStorage", downloadStorage);
|
|
}
|
|
|
|
private NodeRef getRootNode()
|
|
{
|
|
StoreRef storeRef = repoModelsLocation.getStoreRef();
|
|
return nodeService.getRootNode(storeRef);
|
|
}
|
|
|
|
@Override
|
|
public NodeRef getModelNodeRef(String modelName)
|
|
{
|
|
ParameterCheck.mandatoryString("modelName", modelName);
|
|
|
|
StringBuilder builder = new StringBuilder(120);
|
|
builder.append(repoModelsLocation.getPath()).append("//.[@cm:name='").append(modelName).append("' and ")
|
|
.append(RepoAdminServiceImpl.defaultSubtypeOfDictionaryModel).append(']');
|
|
|
|
List<NodeRef> nodeRefs = searchService.selectNodes(getRootNode(), builder.toString(), null, namespaceDAO, false);
|
|
|
|
if (nodeRefs.size() == 0)
|
|
{
|
|
return null;
|
|
}
|
|
else if (nodeRefs.size() > 1)
|
|
{
|
|
// unexpected: should not find multiple nodes with same name
|
|
throw new CustomModelException(MSG_MULTIPLE_MODELS, new Object[] { modelName });
|
|
}
|
|
|
|
return nodeRefs.get(0);
|
|
}
|
|
|
|
private M2Model getM2Model(final NodeRef modelNodeRef)
|
|
{
|
|
ContentReader reader = contentService.getReader(modelNodeRef, ContentModel.PROP_CONTENT);
|
|
if (reader == null)
|
|
{
|
|
return null;
|
|
}
|
|
InputStream in = reader.getContentInputStream();
|
|
try
|
|
{
|
|
return M2Model.createModel(in);
|
|
}
|
|
finally
|
|
{
|
|
if (in != null)
|
|
{
|
|
try
|
|
{
|
|
in.close();
|
|
}
|
|
catch (IOException e)
|
|
{
|
|
logger.error("Failed to close input stream for " + modelNodeRef);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public CustomModelDefinition getCustomModel(String modelName)
|
|
{
|
|
ParameterCheck.mandatoryString("modelName", modelName);
|
|
|
|
Pair<CompiledModel, Boolean> compiledModelPair = getCustomCompiledModel(modelName);
|
|
CustomModelDefinition result = (compiledModelPair == null) ? null : new CustomModelDefinitionImpl(
|
|
compiledModelPair.getFirst(), compiledModelPair.getSecond(), dictionaryService);
|
|
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public ModelDefinition getCustomModelByUri(String namespaceUri)
|
|
{
|
|
ParameterCheck.mandatoryString("namespaceUri", namespaceUri);
|
|
CompiledModel compiledModel = getModelByUri(namespaceUri);
|
|
|
|
if (compiledModel != null)
|
|
{
|
|
return compiledModel.getModelDefinition();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private CompiledModel getModelByUri(String uri)
|
|
{
|
|
for (CompiledModel model : getAllCustomM2Models(false))
|
|
{
|
|
if (uri.equals(getModelNamespaceUriPrefix(model.getM2Model()).getFirst()))
|
|
{
|
|
return model;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Returns compiled custom model and whether the model is active or not as a {@code Pair} object
|
|
*
|
|
* @param modelName the name of the custom model to retrieve
|
|
* @return the {@code Pair<CompiledModel, Boolean>} (or null, if it doesn't exist)
|
|
*/
|
|
protected Pair<CompiledModel, Boolean> getCustomCompiledModel(String modelName)
|
|
{
|
|
ParameterCheck.mandatoryString("modelName", modelName);
|
|
|
|
final NodeRef modelNodeRef = getModelNodeRef(modelName);
|
|
|
|
if (modelNodeRef == null || !nodeService.exists(modelNodeRef))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
M2Model model = null;
|
|
final boolean isActive = Boolean.TRUE.equals(nodeService.getProperty(modelNodeRef, ContentModel.PROP_MODEL_ACTIVE));
|
|
if (isActive)
|
|
{
|
|
QName modelQName = (QName) nodeService.getProperty(modelNodeRef, ContentModel.PROP_MODEL_NAME);
|
|
if (modelQName == null)
|
|
{
|
|
return null;
|
|
}
|
|
try
|
|
{
|
|
CompiledModel compiledModel = dictionaryDAO.getCompiledModel(modelQName);
|
|
model = compiledModel.getM2Model();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
throw new CustomModelException(MSG_RETRIEVE_MODEL, new Object[] { modelName }, e);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
model = getM2Model(modelNodeRef);
|
|
}
|
|
|
|
Pair<CompiledModel, Boolean> result = (model == null) ? null : new Pair<>(compileModel(model), isActive);
|
|
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public PagingResults<CustomModelDefinition> getCustomModels(PagingRequest pagingRequest)
|
|
{
|
|
ParameterCheck.mandatory("pagingRequest", pagingRequest);
|
|
|
|
List<CustomModelDefinition> result = getAllCustomModels();
|
|
return result.isEmpty() ? new EmptyPagingResults<CustomModelDefinition>() : wrapResult(pagingRequest, result);
|
|
}
|
|
|
|
protected List<CustomModelDefinition> getAllCustomModels()
|
|
{
|
|
List<CustomModelDefinition> result = new ArrayList<>();
|
|
|
|
Collection<QName> models = dictionaryDAO.getModels(true);
|
|
|
|
List<String> dictionaryModels = new ArrayList<>();
|
|
for (QName model : models)
|
|
{
|
|
dictionaryModels.add(model.toPrefixString());
|
|
}
|
|
|
|
List<CompiledModel> compiledModels = getAllCustomM2Models(false);
|
|
if (compiledModels.size() > 0)
|
|
{
|
|
for (CompiledModel model : compiledModels)
|
|
{
|
|
// check against models loaded in dictionary
|
|
boolean isActive = false;
|
|
if (dictionaryModels.contains(model.getM2Model().getName()))
|
|
{
|
|
isActive = true;
|
|
}
|
|
result.add(new CustomModelDefinitionImpl(model, isActive, dictionaryService));
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private List<CompiledModel> getAllCustomM2Models(boolean onlyInactiveModels)
|
|
{
|
|
List<CompiledModel> result = new ArrayList<>();
|
|
|
|
StringBuilder builder = new StringBuilder(160);
|
|
builder.append(repoModelsLocation.getPath()).append(RepoAdminServiceImpl.CRITERIA_ALL).append("[(")
|
|
.append(RepoAdminServiceImpl.defaultSubtypeOfDictionaryModel).append(" and ").append(DEFAULT_CUSTOM_MODEL_ASPECT);
|
|
if (onlyInactiveModels)
|
|
{
|
|
builder.append(" and @cm:modelActive='false'");
|
|
}
|
|
builder.append(")]");
|
|
|
|
List<NodeRef> nodeRefs = searchService.selectNodes(getRootNode(), builder.toString(), null, namespaceDAO, false,
|
|
SearchService.LANGUAGE_XPATH);
|
|
|
|
if (nodeRefs.size() > 0)
|
|
{
|
|
for (NodeRef nodeRef : nodeRefs)
|
|
{
|
|
try
|
|
{
|
|
M2Model m2Model = getM2Model(nodeRef);
|
|
if (m2Model == null)
|
|
{
|
|
logger.warn("Couldn't construct M2Model from nodeRef:" + nodeRef);
|
|
continue;
|
|
}
|
|
result.add(compileModel(m2Model));
|
|
}
|
|
catch (Throwable t)
|
|
{
|
|
logger.warn("Skip model (" + t.getMessage() + ")");
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public AspectDefinition getCustomAspect(QName name)
|
|
{
|
|
ParameterCheck.mandatory("name", name);
|
|
|
|
CompiledModel compiledModel = getModelByUri(name.getNamespaceURI());
|
|
if (compiledModel != null)
|
|
{
|
|
return compiledModel.getAspect(name);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public PagingResults<AspectDefinition> getAllCustomAspects(PagingRequest pagingRequest)
|
|
{
|
|
ParameterCheck.mandatory("pagingRequest", pagingRequest);
|
|
|
|
List<AspectDefinition> result = new ArrayList<>();
|
|
List<CompiledModel> list = getAllCustomM2Models(false);
|
|
for (CompiledModel model : list)
|
|
{
|
|
result.addAll(model.getAspects());
|
|
}
|
|
return wrapResult(pagingRequest, result);
|
|
}
|
|
|
|
@Override
|
|
public TypeDefinition getCustomType(QName name)
|
|
{
|
|
ParameterCheck.mandatory("name", name);
|
|
|
|
CompiledModel compiledModel = getModelByUri(name.getNamespaceURI());
|
|
if (compiledModel != null)
|
|
{
|
|
return compiledModel.getType(name);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public PagingResults<TypeDefinition> getAllCustomTypes(PagingRequest pagingRequest)
|
|
{
|
|
ParameterCheck.mandatory("pagingRequest", pagingRequest);
|
|
|
|
List<TypeDefinition> result = new ArrayList<>();
|
|
List<CompiledModel> list = getAllCustomM2Models(false);
|
|
for (CompiledModel model : list)
|
|
{
|
|
result.addAll(model.getTypes());
|
|
}
|
|
return wrapResult(pagingRequest, result);
|
|
}
|
|
|
|
@Override
|
|
public ConstraintDefinition getCustomConstraint(QName name)
|
|
{
|
|
ParameterCheck.mandatory("name", name);
|
|
|
|
CompiledModel compiledModel = getModelByUri(name.getNamespaceURI());
|
|
if (compiledModel != null)
|
|
{
|
|
return compiledModel.getConstraint(name);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public CustomModelDefinition createCustomModel(M2Model m2Model, boolean activate)
|
|
{
|
|
ParameterCheck.mandatory("m2Model", m2Model);
|
|
|
|
String modelName = m2Model.getName();
|
|
int colonIndex = modelName.indexOf(QName.NAMESPACE_PREFIX);
|
|
final String modelFileName = (colonIndex == -1) ? modelName : modelName.substring(colonIndex + 1);
|
|
|
|
if (isModelExists(modelFileName))
|
|
{
|
|
throw new CustomModelException.ModelExistsException(MSG_NAME_ALREADY_IN_USE, new Object[] { modelFileName });
|
|
}
|
|
|
|
// Validate the model namespace URI
|
|
validateModelNamespaceUri(getModelNamespaceUriPrefix(m2Model).getFirst());
|
|
// Validate the model namespace prefix
|
|
validateModelNamespacePrefix(getModelNamespaceUriPrefix(m2Model).getSecond());
|
|
|
|
// Return the created model definition
|
|
CompiledModel compiledModel = createUpdateModel(modelFileName, m2Model, activate, MSG_CREATE_MODEL_ERR, false);
|
|
CustomModelDefinition modelDef = new CustomModelDefinitionImpl(compiledModel, activate, dictionaryService);
|
|
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
logger.debug(modelFileName + " model has been created.");
|
|
}
|
|
return modelDef;
|
|
}
|
|
|
|
@Override
|
|
public CustomModelDefinition updateCustomModel(String modelFileName, M2Model m2Model, boolean activate)
|
|
{
|
|
ParameterCheck.mandatory("m2Model", m2Model);
|
|
|
|
final NodeRef existingModelNodeRef = getModelNodeRef(modelFileName);
|
|
if (existingModelNodeRef == null)
|
|
{
|
|
throw new CustomModelException.ModelDoesNotExistException(MSG_MODEL_NOT_EXISTS, new Object[] { modelFileName });
|
|
}
|
|
// Existing model property and namespace uri-prefix pair
|
|
final boolean isActive = Boolean.TRUE.equals(nodeService.getProperty(existingModelNodeRef, ContentModel.PROP_MODEL_ACTIVE));
|
|
final M2Model existingModel = getM2Model(existingModelNodeRef);
|
|
final Pair<String, String> existingNamespacePair = getModelNamespaceUriPrefix(existingModel);
|
|
// New model namespace uri-prefix pair
|
|
final Pair<String, String> newNamespacePair = getModelNamespaceUriPrefix(m2Model);
|
|
|
|
if (isActive && !(existingNamespacePair.equals(newNamespacePair)))
|
|
{
|
|
throw new CustomModelException.ActiveModelConstraintException(MSG_NAMESPACE_ACTIVE_MODEL);
|
|
}
|
|
|
|
// if the prefix has changed, then check the new prefix is not in use.
|
|
if (!existingNamespacePair.getSecond().equals(newNamespacePair.getSecond()))
|
|
{
|
|
validateModelNamespacePrefix(newNamespacePair.getSecond());
|
|
}
|
|
|
|
// if the URI has changed, then check the new URI is not in use.
|
|
if (!existingNamespacePair.getFirst().equals(newNamespacePair.getFirst()))
|
|
{
|
|
validateModelNamespaceUri(newNamespacePair.getFirst());
|
|
}
|
|
|
|
/*
|
|
* We set the requiresNewTx = true, in order to catch any exception
|
|
* thrown within the low level content model management.
|
|
* For example, deleting a property of an active model, where the
|
|
* property has been applied to a node will cause the
|
|
* ModelValidatorImpl to throw an exception.
|
|
* Without starting a new TX, we can't catch that exception.
|
|
*/
|
|
CompiledModel compiledModel = createUpdateModel(modelFileName, m2Model, activate, MSG_UPDATE_MODEL_ERR, true);
|
|
CustomModelDefinition modelDef = new CustomModelDefinitionImpl(compiledModel, activate, dictionaryService);
|
|
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
logger.debug(modelFileName + " model has been updated.");
|
|
}
|
|
return modelDef;
|
|
}
|
|
|
|
private CompiledModel createUpdateModel(final String modelFileName, final M2Model m2Model, final boolean activate, String errMsgId, boolean requiresNewTx)
|
|
{
|
|
// Validate model
|
|
CompiledModel compiledModel = compileModel(m2Model);
|
|
|
|
// Validate properties default values
|
|
validatePropsDefaultValues(compiledModel);
|
|
|
|
ByteArrayOutputStream xml = new ByteArrayOutputStream();
|
|
m2Model.toXML(xml);
|
|
final InputStream modelStream = new ByteArrayInputStream(xml.toByteArray());
|
|
|
|
// Create the model node
|
|
NodeRef nodeRef = doInTransaction(errMsgId, requiresNewTx, new RetryingTransactionCallback<NodeRef>()
|
|
{
|
|
public NodeRef execute() throws Exception
|
|
{
|
|
return repoAdminService.deployModel(modelStream, modelFileName, activate);
|
|
}
|
|
});
|
|
|
|
if (!nodeService.hasAspect(nodeRef, ASPECT_CUSTOM_MODEL))
|
|
{
|
|
// Add the 'customModelManagement' marker aspect, to
|
|
// indicate that this model has been created dynamically by this service
|
|
nodeService.addAspect(nodeRef, ASPECT_CUSTOM_MODEL, null);
|
|
}
|
|
// Add hidden aspect
|
|
if (!hiddenAspect.hasHiddenAspect(nodeRef))
|
|
{
|
|
hiddenAspect.hideNode(nodeRef, false, false, false);
|
|
}
|
|
|
|
return compiledModel;
|
|
}
|
|
|
|
/**
|
|
* Validates the properties' non-null default values against the defined property constraints.
|
|
*
|
|
* @param compiledModel the compiled model
|
|
* @throws CustomModelException.CustomModelConstraintException if there is constraint evaluation
|
|
* exception
|
|
*/
|
|
private void validatePropsDefaultValues(CompiledModel compiledModel)
|
|
{
|
|
for (PropertyDefinition propertyDef : compiledModel.getProperties())
|
|
{
|
|
if (propertyDef.getDefaultValue() != null && propertyDef.getConstraints().size() > 0)
|
|
{
|
|
for (ConstraintDefinition constraintDef : propertyDef.getConstraints())
|
|
{
|
|
Constraint constraint = constraintDef.getConstraint();
|
|
try
|
|
{
|
|
constraint.evaluate(propertyDef.getDefaultValue());
|
|
}
|
|
catch (AlfrescoRuntimeException ex)
|
|
{
|
|
String message = getRootCauseMsg(ex, false, "cmm.service.constraint.default_prop_value_err");
|
|
throw new CustomModelException.CustomModelConstraintException(message);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public CompiledModel compileModel(M2Model m2Model)
|
|
{
|
|
try
|
|
{
|
|
// Validate model dependencies, constraints and etc. before creating a node
|
|
return m2Model.compile(dictionaryDAO, namespaceDAO, true);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
AlfrescoRuntimeException alf = null;
|
|
if (ex instanceof AlfrescoRuntimeException)
|
|
{
|
|
alf = (AlfrescoRuntimeException) ex;
|
|
}
|
|
else
|
|
{
|
|
alf = AlfrescoRuntimeException.create(ex, ex.getMessage());
|
|
}
|
|
|
|
Throwable cause = alf.getRootCause();
|
|
String message = null;
|
|
|
|
if (cause instanceof DuplicateDefinitionException)
|
|
{
|
|
message = getRootCauseMsg(cause, false, MSG_INVALID_MODEL);
|
|
throw new CustomModelException.CustomModelConstraintException(message);
|
|
}
|
|
else
|
|
{
|
|
message = getRootCauseMsg(cause, true, null);
|
|
throw new CustomModelException.InvalidCustomModelException(MSG_INVALID_MODEL, new Object[] { message }, ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected <T> PagingResults<T> wrapResult(PagingRequest pagingRequest, List<T> result)
|
|
{
|
|
final int totalSize = result.size();
|
|
final PageDetails pageDetails = PageDetails.getPageDetails(pagingRequest, totalSize);
|
|
|
|
final List<T> page = new ArrayList<>(pageDetails.getPageSize());
|
|
Iterator<T> it = result.iterator();
|
|
for (int counter = 0; counter < pageDetails.getEnd() && it.hasNext(); counter++)
|
|
{
|
|
T element = it.next();
|
|
if (counter < pageDetails.getSkipCount())
|
|
{
|
|
continue;
|
|
}
|
|
if (counter > pageDetails.getEnd() - 1)
|
|
{
|
|
break;
|
|
}
|
|
page.add(element);
|
|
}
|
|
|
|
return new PagingResults<T>()
|
|
{
|
|
@Override
|
|
public List<T> getPage()
|
|
{
|
|
return page;
|
|
}
|
|
|
|
@Override
|
|
public boolean hasMoreItems()
|
|
{
|
|
return pageDetails.hasMoreItems();
|
|
}
|
|
|
|
@Override
|
|
public Pair<Integer, Integer> getTotalResultCount()
|
|
{
|
|
Integer total = Integer.valueOf(totalSize);
|
|
return new Pair<>(total, total);
|
|
}
|
|
|
|
@Override
|
|
public String getQueryExecutionId()
|
|
{
|
|
return null;
|
|
}
|
|
};
|
|
}
|
|
|
|
@Override
|
|
public boolean isModelAdmin(String userName)
|
|
{
|
|
if (userName == null)
|
|
{
|
|
return false;
|
|
}
|
|
return this.authorityService.isAdminAuthority(userName)
|
|
|| this.authorityService.getAuthoritiesForUser(userName).contains(GROUP_ALFRESCO_MODEL_ADMINISTRATORS_AUTHORITY);
|
|
}
|
|
|
|
@Override
|
|
public void activateCustomModel(String modelName)
|
|
{
|
|
try
|
|
{
|
|
repoAdminService.activateModel(modelName);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new CustomModelException(MSG_UNABLE_MODEL_ACTIVATE, new Object[] { modelName }, ex);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void deactivateCustomModel(final String modelName)
|
|
{
|
|
CustomModelDefinition customModelDefinition = getCustomModel(modelName);
|
|
if (customModelDefinition == null)
|
|
{
|
|
throw new CustomModelException.ModelDoesNotExistException(MSG_MODEL_NOT_EXISTS, new Object[] { modelName });
|
|
}
|
|
|
|
Collection<TypeDefinition> modelTypes = customModelDefinition.getTypeDefinitions();
|
|
Collection<AspectDefinition> modelAspects = customModelDefinition.getAspectDefinitions();
|
|
|
|
for (CompiledModel cm : getAllCustomM2Models(false))
|
|
{
|
|
// Ignore type/aspect dependency check within the model itself
|
|
if (!customModelDefinition.getName().equals(cm.getModelDefinition().getName()))
|
|
{
|
|
// Check if the type of the model being deactivated is the parent of another model's type
|
|
validateTypeAspectDependency(modelTypes, cm.getTypes());
|
|
|
|
// Check if the aspect of the model being deactivated is the parent of another model's aspect
|
|
validateTypeAspectDependency(modelAspects, cm.getAspects());
|
|
}
|
|
}
|
|
|
|
// requiresNewTx = true, in order to catch any exception thrown within
|
|
// "DictionaryModelType$DictionaryModelTypeTransactionListener" model validation.
|
|
doInTransaction(MSG_UNABLE_MODEL_DEACTIVATE, true, new RetryingTransactionCallback<Void>()
|
|
{
|
|
public Void execute() throws Exception
|
|
{
|
|
repoAdminService.deactivateModel(modelName);
|
|
return null;
|
|
}
|
|
});
|
|
}
|
|
|
|
private void validateTypeAspectDependency(Collection<? extends ClassDefinition> parentDefs, Collection<? extends ClassDefinition> childDefs)
|
|
{
|
|
for (ClassDefinition parentClassDef : parentDefs)
|
|
{
|
|
for (ClassDefinition childClassDef : childDefs)
|
|
{
|
|
if (parentClassDef.getName().equals(childClassDef.getParentName()))
|
|
{
|
|
Object[] msgParams = new Object[] { parentClassDef.getName().toPrefixString(),
|
|
childClassDef.getName().toPrefixString(),
|
|
childClassDef.getModel().getName().getLocalName() };
|
|
|
|
if (parentClassDef instanceof TypeDefinition)
|
|
{
|
|
throw new CustomModelException.CustomModelConstraintException(MSG_FAILED_DEACTIVATION_TYPE_DEPENDENCY, msgParams);
|
|
}
|
|
else
|
|
{
|
|
throw new CustomModelException.CustomModelConstraintException(MSG_FAILED_DEACTIVATION_ASPECT_DEPENDENCY, msgParams);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void deleteCustomModel(String modelName)
|
|
{
|
|
NodeRef nodeRef = getModelNodeRef(modelName);
|
|
if (nodeRef == null)
|
|
{
|
|
throw new CustomModelException.ModelDoesNotExistException(MSG_MODEL_NOT_EXISTS, new Object[] { modelName });
|
|
}
|
|
|
|
final boolean isActive = Boolean.TRUE.equals(nodeService.getProperty(nodeRef, ContentModel.PROP_MODEL_ACTIVE));
|
|
if (isActive)
|
|
{
|
|
throw new CustomModelException.ActiveModelConstraintException(MSG_UNABLE_DELETE_ACTIVE_MODEL);
|
|
}
|
|
try
|
|
{
|
|
repoAdminService.undeployModel(modelName);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new CustomModelException(MSG_UNABLE_MODEL_DELETE, new Object[] { modelName }, ex);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean isNamespaceUriExists(String modelNamespaceUri)
|
|
{
|
|
ParameterCheck.mandatoryString("modelNamespaceUri", modelNamespaceUri);
|
|
|
|
Collection<String> uris = namespaceDAO.getURIs();
|
|
if (uris.contains(modelNamespaceUri))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
for (CompiledModel model : getAllCustomM2Models(false))
|
|
{
|
|
if (modelNamespaceUri.equals(getModelNamespaceUriPrefix(model.getM2Model()).getFirst()))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean isNamespacePrefixExists(String modelNamespacePrefix)
|
|
{
|
|
ParameterCheck.mandatoryString("modelNamespacePrefix", modelNamespacePrefix);
|
|
|
|
Collection<String> prefixes = namespaceDAO.getPrefixes();
|
|
if (prefixes.contains(modelNamespacePrefix))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
for (CompiledModel model : getAllCustomM2Models(false))
|
|
{
|
|
if (modelNamespacePrefix.equals(getModelNamespaceUriPrefix(model.getM2Model()).getSecond()))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean isModelExists(String modelFileName)
|
|
{
|
|
NodeRef nodeRef = getModelNodeRef(modelFileName);
|
|
if (nodeRef != null)
|
|
{
|
|
return true;
|
|
}
|
|
// Also check against the bootstrapped models
|
|
for (QName qname : dictionaryService.getAllModels())
|
|
{
|
|
if (qname.getLocalName().equalsIgnoreCase(modelFileName))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private Pair<String, String> getModelNamespaceUriPrefix(M2Model model)
|
|
{
|
|
ParameterCheck.mandatory("model", model);
|
|
|
|
List<M2Namespace> namespaces = model.getNamespaces();
|
|
if (namespaces.isEmpty())
|
|
{
|
|
throw new CustomModelException.InvalidNamespaceException(MSG_NAMESPACE_NOT_EXISTS, new Object[] { model.getName() });
|
|
}
|
|
if (namespaces.size() > 1)
|
|
{
|
|
throw new CustomModelException.InvalidNamespaceException(MSG_NAMESPACE_MANY_EXIST, new Object[] { model.getName() });
|
|
}
|
|
M2Namespace ns = namespaces.iterator().next();
|
|
|
|
return new Pair<>(ns.getUri(), ns.getPrefix());
|
|
}
|
|
|
|
private void validateModelNamespaceUri(String uri)
|
|
{
|
|
if (isNamespaceUriExists(uri))
|
|
{
|
|
throw new CustomModelException.NamespaceConstraintException(MSG_NAMESPACE_URI_ALREADY_IN_USE, new Object[] { uri });
|
|
}
|
|
}
|
|
|
|
private void validateModelNamespacePrefix(String prefix)
|
|
{
|
|
if (isNamespacePrefixExists(prefix))
|
|
{
|
|
throw new CustomModelException.NamespaceConstraintException(MSG_NAMESPACE_PREFIX_ALREADY_IN_USE, new Object[] { prefix });
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A helper method to run a unit of work in a transaction.
|
|
*
|
|
* @param errMsgId message id for the new wrapper exception ({@link CustomModelException})
|
|
* when an exception occurs
|
|
* @param requiresNewTx <tt>true</tt> to force a new transaction or
|
|
* <tt>false</tt> to partake in any existing transaction
|
|
* @param cb The callback containing the unit of work
|
|
* @return Returns the result of the unit of work
|
|
*/
|
|
private <R> R doInTransaction(String errMsgId, boolean requiresNewTx, RetryingTransactionCallback<R> cb)
|
|
{
|
|
try
|
|
{
|
|
return retryingTransactionHelper.doInTransaction(cb, false, requiresNewTx);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
AlfrescoRuntimeException alf = null;
|
|
if (ex instanceof AlfrescoRuntimeException)
|
|
{
|
|
alf = (AlfrescoRuntimeException) ex;
|
|
}
|
|
else
|
|
{
|
|
alf = AlfrescoRuntimeException.create(ex, ex.getMessage());
|
|
}
|
|
|
|
Throwable cause = alf.getRootCause();
|
|
String message = getRootCauseMsg(cause, true, null);
|
|
|
|
throw new CustomModelException(errMsgId, new Object[] { message }, ex);
|
|
}
|
|
}
|
|
|
|
private static String getRootCauseMsg(Throwable cause, boolean withAlfLogNum, String defaultMsg)
|
|
{
|
|
if (defaultMsg == null)
|
|
{
|
|
defaultMsg = "";
|
|
}
|
|
|
|
String message = cause.getMessage();
|
|
if(message == null)
|
|
{
|
|
return defaultMsg;
|
|
}
|
|
else
|
|
{
|
|
return ((withAlfLogNum) ? message : message.replaceFirst("\\d+", "").trim());
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public NodeRef createDownloadNode(final String modelFileName, boolean withAssociatedForm)
|
|
{
|
|
List<NodeRef> nodesToBeDownloaded = new ArrayList<>(2);
|
|
|
|
NodeRef customModelNodeRef = getModelNodeRef(modelFileName);
|
|
if(customModelNodeRef == null)
|
|
{
|
|
throw new CustomModelException.ModelDoesNotExistException(MSG_MODEL_NOT_EXISTS, new Object[] { modelFileName });
|
|
}
|
|
// We create a copy of the model, so we can rename it, change its
|
|
// content type and move it to the download container in order to be
|
|
// cleaned by the download service cleanup job.
|
|
customModelNodeRef = createCustomModelCopy(modelFileName + ".xml", customModelNodeRef);
|
|
nodesToBeDownloaded.add(customModelNodeRef);
|
|
|
|
if (withAssociatedForm)
|
|
{
|
|
NodeRef shareExtModuleNodeRef = null;
|
|
try
|
|
{
|
|
shareExtModuleNodeRef = createCustomModelShareExtModuleRef(modelFileName);
|
|
nodesToBeDownloaded.add(shareExtModuleNodeRef);
|
|
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
StringBuilder msg = new StringBuilder();
|
|
msg.append("Temp nodes created for download: Custom model nodeRef [")
|
|
.append(customModelNodeRef)
|
|
.append("] and its associated Share form nodeRef [")
|
|
.append(shareExtModuleNodeRef).append(']');
|
|
logger.debug(msg.toString());
|
|
}
|
|
}
|
|
catch (CustomModelException ex)
|
|
{
|
|
// We don't throw the exception as the Model might be a
|
|
// draft model and might have never been activated or never had any forms created for it.
|
|
// So in this case we just construct the zip containing only the model.
|
|
StringBuilder msg = new StringBuilder();
|
|
msg.append("Constructing CMM zip file containing only the model [")
|
|
.append(modelFileName)
|
|
.append(".xml] without its associated share extension module, because: ")
|
|
.append(ex.getMessage());
|
|
|
|
logger.warn(msg.toString());
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
StringBuilder msg = new StringBuilder();
|
|
msg.append("Temp node created for download: Custom model nodeRef [")
|
|
.append(customModelNodeRef).append(']');
|
|
logger.debug(msg.toString());
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
NodeRef archiveNodeRef = downloadService.createDownload(nodesToBeDownloaded.toArray(new NodeRef[nodesToBeDownloaded.size()]), false);
|
|
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
StringBuilder msg = new StringBuilder();
|
|
msg.append("Created download nodeRef [").append(archiveNodeRef).append(']');
|
|
logger.debug(msg.toString());
|
|
}
|
|
|
|
return archiveNodeRef;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new CustomModelException("cmm.service.download.create_err", ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Finds the {@code module} element within the Share persisted-extension
|
|
* XML file and then writes the XML fragment as the content of a newly created node.
|
|
*
|
|
* @param modelName the model name
|
|
* @return the created nodeRef
|
|
*/
|
|
protected NodeRef createCustomModelShareExtModuleRef(final String modelName)
|
|
{
|
|
final String moduleId = "CMM_" + modelName;
|
|
|
|
final NodeRef formNodeRef = getShareExtModule();
|
|
ContentReader reader = contentService.getReader(formNodeRef, ContentModel.PROP_CONTENT);
|
|
|
|
if (reader == null)
|
|
{
|
|
throw new CustomModelException("cmm.service.download.share_ext_node_read_err");
|
|
}
|
|
|
|
InputStream in = reader.getContentInputStream();
|
|
Node moduleIdXmlNode = null;
|
|
try
|
|
{
|
|
Document document = XMLUtil.parse(in); // the stream will be closed
|
|
|
|
final String xpathQuery = "/extension//modules//module//id[.= '" + moduleId + "']";
|
|
|
|
XPath xPath = XPathFactory.newInstance().newXPath();
|
|
XPathExpression expression = xPath.compile(xpathQuery);
|
|
|
|
moduleIdXmlNode = (Node) expression.evaluate(document, XPathConstants.NODE);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new CustomModelException("cmm.service.download.share_ext_file_parse_err", ex);
|
|
}
|
|
|
|
if (moduleIdXmlNode == null)
|
|
{
|
|
throw new CustomModelException("cmm.service.download.share_ext_module_not_found", new Object[] { moduleId });
|
|
}
|
|
|
|
final File moduleFile = TempFileProvider.createTempFile(moduleId, ".xml");
|
|
try
|
|
{
|
|
XMLUtil.print(moduleIdXmlNode.getParentNode(), moduleFile);
|
|
}
|
|
catch (IOException error)
|
|
{
|
|
throw new CustomModelException("cmm.service.download.share_ext_write_err", new Object[] { moduleId }, error);
|
|
}
|
|
|
|
return doInTransaction(MSG_DOWNLOAD_CREATE_SHARE_EXT_ERR, true, new RetryingTransactionCallback<NodeRef>()
|
|
{
|
|
@Override
|
|
public NodeRef execute() throws Exception
|
|
{
|
|
final NodeRef nodeRef = createDownloadTypeNode(moduleId + SHARE_EXT_MODULE_SUFFIX);
|
|
ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true);
|
|
writer.setMimetype(MimetypeMap.MIMETYPE_XML);
|
|
writer.setEncoding("UTF-8");
|
|
writer.putContent(moduleFile);
|
|
|
|
return nodeRef;
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Gets Share persisted-extension nodeRef
|
|
*/
|
|
protected NodeRef getShareExtModule()
|
|
{
|
|
List<NodeRef> results = searchService.selectNodes(getRootNode(), this.shareExtModulePath, null, this.namespaceDAO, false,
|
|
SearchService.LANGUAGE_XPATH);
|
|
|
|
if (results.isEmpty())
|
|
{
|
|
throw new CustomModelException("cmm.service.download.share_ext_file_not_found");
|
|
}
|
|
|
|
return results.get(0);
|
|
}
|
|
|
|
/**
|
|
* Creates a copy of the custom model where the created node will be a child
|
|
* of download container.
|
|
*
|
|
* @param newName the model new name
|
|
* @param modelNodeRef existing model nodeRef
|
|
* @return the created nodeRef
|
|
*/
|
|
protected NodeRef createCustomModelCopy(final String newName, final NodeRef modelNodeRef)
|
|
{
|
|
return doInTransaction(MSG_DOWNLOAD_COPY_MODEL_ERR, true, new RetryingTransactionCallback<NodeRef>()
|
|
{
|
|
@Override
|
|
public NodeRef execute() throws Exception
|
|
{
|
|
final NodeRef newNodeRef = createDownloadTypeNode(newName);
|
|
Serializable content = nodeService.getProperty(modelNodeRef, ContentModel.PROP_CONTENT);
|
|
nodeService.setProperty(newNodeRef, ContentModel.PROP_CONTENT, content);
|
|
|
|
return newNodeRef;
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Creates node with a type {@link DownloadModel#TYPE_DOWNLOAD} within the
|
|
* download container (see
|
|
* {@link DownloadStorage#getOrCreateDowloadContainer()} )
|
|
* <p>
|
|
* Also, the {@code IndexControlAspect} is applied to the created node.
|
|
*
|
|
* @param name the node name
|
|
* @return the created nodeRef
|
|
*/
|
|
private NodeRef createDownloadTypeNode(final String name)
|
|
{
|
|
final NodeRef newNodeRef = nodeService.createNode(
|
|
downloadStorage.getOrCreateDowloadContainer(),
|
|
ContentModel.ASSOC_CHILDREN,
|
|
ContentModel.ASSOC_CHILDREN,
|
|
DownloadModel.TYPE_DOWNLOAD,
|
|
Collections.<QName, Serializable> singletonMap(ContentModel.PROP_NAME, name)).getChildRef();
|
|
|
|
Map<QName, Serializable> aspectProperties = new HashMap<>(2);
|
|
aspectProperties.put(ContentModel.PROP_IS_INDEXED, Boolean.FALSE);
|
|
aspectProperties.put(ContentModel.PROP_IS_CONTENT_INDEXED, Boolean.FALSE);
|
|
nodeService.addAspect(newNodeRef, ContentModel.ASPECT_INDEX_CONTROL, aspectProperties);
|
|
|
|
return newNodeRef;
|
|
}
|
|
|
|
@Override
|
|
public CustomModelsInfo getCustomModelsInfo()
|
|
{
|
|
List<CustomModelDefinition> page = getCustomModels(new PagingRequest(0, Integer.MAX_VALUE)).getPage();
|
|
|
|
int activeModels = 0;
|
|
int activeTypes = 0;
|
|
int activeAspects = 0;
|
|
for (CustomModelDefinition cm : page)
|
|
{
|
|
if (cm.isActive())
|
|
{
|
|
activeModels++;
|
|
activeTypes += cm.getTypeDefinitions().size();
|
|
activeAspects += cm.getAspectDefinitions().size();
|
|
}
|
|
}
|
|
|
|
CustomModelsInfo info = new CustomModelsInfo();
|
|
info.setNumberOfActiveModels(activeModels);
|
|
info.setNumberOfActiveTypes(activeTypes);
|
|
info.setNumberOfActiveAspects(activeAspects);
|
|
return info;
|
|
}
|
|
}
|