package org.easysoa.registry;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.log4j.Logger;
import org.easysoa.registry.subproject.SubprojectId;
import org.easysoa.registry.types.Project;
import org.easysoa.registry.types.Repository;
import org.easysoa.registry.types.Subproject;
import org.easysoa.registry.types.SubprojectNode;
import org.easysoa.registry.types.ids.SoaNodeId;
import org.easysoa.registry.utils.NuxeoListUtils;
import org.nuxeo.ecm.core.api.ClientException;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.DocumentModelIterator;
import org.nuxeo.ecm.core.api.DocumentModelList;
import org.nuxeo.ecm.core.api.IdRef;
import org.nuxeo.ecm.core.api.PathRef;
import org.nuxeo.ecm.core.api.VersioningOption;
import org.nuxeo.ecm.core.api.event.DocumentEventTypes;
import org.nuxeo.ecm.core.api.model.PropertyException;
import org.nuxeo.ecm.core.event.Event;
import org.nuxeo.snapshot.Snapshot;
import org.nuxeo.snapshot.Snapshotable;
public class SubprojectServiceImpl {
public static final PathRef defaultProjectPathRef = new PathRef(Project.DEFAULT_PROJECT_PATH);
///public static final PathRef defaultSubprojectPathRef = new PathRef(Subproject.DEFAULT_SUBPROJECT_PATH);
private static Logger logger = Logger.getLogger(SubprojectServiceImpl.class);
/**
* Creates but doesn't save session
* @param documentManager
* @param name
* @return
* @throws ClientException
*/
public static DocumentModel createProject(CoreSession documentManager, String name) throws ClientException {
DocumentModel projectModel = documentManager.createDocumentModel(Project.DOCTYPE);
projectModel.setPathInfo(Repository.DEFAULT_DOMAIN_PATH, name); // TODO safeName()
projectModel.setPropertyValue("dc:title", name);
projectModel = documentManager.createDocument(projectModel);
return projectModel;
}
/**
* Creates but doesn't save session
* @param documentManager
* @param name
* @param projectModel
* @param parentSubprojectModels
* @return
* @throws ClientException
*/
public static DocumentModel createSubproject(CoreSession documentManager, String name,
DocumentModel projectModel, List<DocumentModel> parentSubprojectModels) throws ClientException {
DocumentModel subprojectModel = documentManager.createDocumentModel(Subproject.DOCTYPE); // triggers DocumentEventTypes.EMPTY_DOCUMENTMODEL_CREATED
subprojectModel.setPathInfo(projectModel.getPathAsString(), name); // TODO safeName()
subprojectModel.setPropertyValue("dc:title", name);
String[] parentSubprojectEcmUuids;
if (parentSubprojectModels == null) {
parentSubprojectEcmUuids = NuxeoListUtils.EMPTY_STRING_ARRAY;
} else {
parentSubprojectEcmUuids = NuxeoListUtils.getIds(parentSubprojectModels).toArray(NuxeoListUtils.EMPTY_STRING_ARRAY);
}
subprojectModel.setPropertyValue(Subproject.XPATH_PARENT_SUBPROJECTS, parentSubprojectEcmUuids);
onSubprojectAboutToCreate(documentManager, subprojectModel);
subprojectModel = documentManager.createDocument(subprojectModel);
return subprojectModel;
}
/**
* To be called on aboutToCreate
* @param subprojectModel
* @param documentManager
* @param parentSubprojectEcmUuids
* @throws ClientException
*/
public static void onSubprojectAboutToCreate(CoreSession documentManager,
DocumentModel subprojectModel) throws ClientException {
String subprojectId = buildSubprojectId(subprojectModel);
if (getSubprojectById(documentManager, subprojectId) != null) {
throw new ClientException("Trying to create Subproject but already exists at "
+ subprojectId);
}
subprojectModel.setPropertyValue(Subproject.XPATH_SUBPROJECT, subprojectId); // to get copied
// TODO also on document change... (parents)
computeAndSetVisibleSubprojects(documentManager, subprojectModel);
// NB. saveDocument() is auto done after aboutToCreate even by event system
// TODO on documentCreated, update all below
}
public static String buildSubprojectId(DocumentModel subprojectModel) {
String subprojectVersionSuffix;
if (subprojectModel.isVersion()) {
// TODO NB. never happens on aboutToCreate, but on aboutToCreateLeafVersionEvent
subprojectVersionSuffix = subprojectModel.getVersionLabel();
} else {
subprojectVersionSuffix = null;
}
return buildSubprojectId(subprojectModel.getPathAsString(), null, subprojectVersionSuffix);
}
/**
* TODO not used for now
* @param projectPath projectPath (or subprojectPath)
* @param subprojectName can be null if provided in projectPath
* @param version version of versioned Phase / subproject, or null for live
* @return
*/
public static String buildSubprojectId(String projectPath, String subprojectName, String version) {
StringBuffer sbuf = new StringBuffer(projectPath);
if (subprojectName != null && !subprojectName.isEmpty()) {
sbuf.append('/');
sbuf.append(subprojectName);
}
sbuf.append(Subproject.SUBPROJECT_ID_VERSION_SEPARATOR);
if (version != null) {
sbuf.append(version);
} // else live : Phase / subproject id ends with _v
return sbuf.toString();
}
/**
*
* @param subprojectId
* @return null if not a subprojectId (ex. context without selected subproject)
*/
public static SubprojectId parseSubprojectId(String subprojectId) {
int slashIndex = subprojectId.lastIndexOf('/');
int versionIndex = subprojectId.lastIndexOf('_'); // NB. always there at the end in "_v"
if (slashIndex == -1 || versionIndex == -1) {
return null; // not a subproject id
}
int prefixLength = Repository.DEFAULT_DOMAIN_PATH.length();
String projectName = subprojectId.substring(prefixLength + 1, slashIndex);
String subprojectName = subprojectId.substring(slashIndex + 1, versionIndex);
String version = subprojectId.substring(versionIndex + 2, subprojectId.length());
return new SubprojectId(projectName, subprojectName, version);
}
public static String getPathFromId(String subprojectId) {
int lastVersionSeparatorId = subprojectId.lastIndexOf(Subproject.SUBPROJECT_ID_VERSION_SEPARATOR);
if (lastVersionSeparatorId < 0) {
logger.warn("getPathFromId() called on badly formatted subprojectId " + subprojectId);
return null;
}
return subprojectId.substring(0, lastVersionSeparatorId);
}
public static boolean isBeingVersionedSubprojectNodeEvent(Event event, DocumentModel subprojectNode)
throws PropertyException, ClientException {
if (!DocumentEventTypes.DOCUMENT_UPDATED.equals(event.getName())) {
return false;
}
String subprojectId = (String) subprojectNode.getPropertyValue(SubprojectNode.XPATH_SUBPROJECT);
DocumentModel subprojectInDatabase = getSubprojectById(subprojectNode.getCoreSession(), subprojectId);
return subprojectInDatabase == null; // subproject ID about to change & being propagated but not yet saved
/*
String subprojectId = (String) context.getPropertyValue(SubprojectNode.XPATH_SUBPROJECT);
int lastVersionSeparatorId = subprojectId.lastIndexOf(Subproject.SUBPROJECT_ID_VERSION_SEPARATOR);
if (lastVersionSeparatorId < 0) {
logger.warn("isBeingVersionedSubprojectNode() called on badly formatted subprojectId " + subprojectId);
}
return lastVersionSeparatorId != subprojectId.length() - Subproject.SUBPROJECT_ID_VERSION_SEPARATOR.length();
*/
}
/**
* Uses db & spnode:subproject. To rather use getParent(), use getSubprojectOfNode()
* @param documentManager
* @param subprojectId
* @return
* @throws ClientException
*/
public static DocumentModel getSubprojectById(CoreSession documentManager, String subprojectId) throws ClientException {
subprojectId = getSubprojectIdOrCreateDefault(documentManager, subprojectId);
return getSubprojectByIdInternal(documentManager, subprojectId);
}
protected static DocumentModel getSubprojectByIdInternal(CoreSession documentManager, String subprojectId) throws ClientException {
// NB. three differents ways :
// 1. looking in db using path : NO doesn't work for versioned Phase / subproject because their Path is relative
/*int lastVersionSeparatorId = subprojectId.lastIndexOf(Subproject.SUBPROJECT_ID_VERSION_SEPARATOR);
if (lastVersionSeparatorId < 0) {
logger.warn("getSubprojectById() called on badly formatted subprojectId " + subprojectId);
return null;
}
String path = subprojectId.substring(0, lastVersionSeparatorId);
String versionLabelCriteria;
if (lastVersionSeparatorId == subprojectId.length() - Subproject.SUBPROJECT_ID_VERSION_SEPARATOR.length()) {
versionLabelCriteria = "";
} else {
String versionLabel = subprojectId.substring(lastVersionSeparatorId + Subproject.SUBPROJECT_ID_VERSION_SEPARATOR.length());
versionLabelCriteria = " AND ecm:versionLabel='" + versionLabel + "'";
}
DocumentModelList res = documentManager.query(DocumentService.NXQL_SELECT_FROM
+ DocumentService.NXQL_WHERE_NO_PROXY + + DocumentService.NXQL_AND
+ "Subproject where ecm:path='" + path + "'" + versionLabelCriteria); // NO path can't be used cross Phase/subproject */
// 2.(rather) looking in db for Subproject with spnode:subprojet (simpler and more generic than using path)
String request = DocumentService.NXQL_SELECT_FROM + Subproject.DOCTYPE
+ DocumentService.NXQL_WHERE_NO_PROXY + DocumentService.NXQL_AND
+ SubprojectNode.XPATH_SUBPROJECT + "='" + subprojectId + "'";
DocumentModelList res = documentManager.query(request);
if (res.size() == 1) {
return res.get(0);
} else if (res.isEmpty()) {
return null;
} else {
throw new ClientException("More than one subproject found for id " + subprojectId + " : " + res);
}
// 3. using getParent() => rather use getSubprojectOfNode()
}
// 1.1 using only Path from name : NO doesn't work for versioned Phase / subproject because their Path is relative
public static DocumentModel getSubprojectByNameAndVersion(CoreSession documentManager,
DocumentModel project, String subprojectName, String version) throws ClientException {
String subprojectId = buildSubprojectId(project.getPathAsString(), subprojectName, version);
return getSubprojectByIdInternal(documentManager, subprojectId);
}
/**
* TODO only set if changed & then return true
* TODO not public ??
*/
public static void computeAndSetVisibleSubprojects(CoreSession documentManager, DocumentModel subprojectModel) throws ClientException {
String subprojectId = (String) subprojectModel.getPropertyValue(SubprojectNode.XPATH_SUBPROJECT);
Serializable foundParentSubprojectIds = subprojectModel.getPropertyValue(Subproject.XPATH_PARENT_SUBPROJECTS);
String[] parentSubprojectIds;
if (foundParentSubprojectIds instanceof String) {
// for easier remote initialization
parentSubprojectIds = ((String) foundParentSubprojectIds).split(",");
subprojectModel.setPropertyValue(Subproject.XPATH_PARENT_SUBPROJECTS, parentSubprojectIds);
} else {
parentSubprojectIds = (String[]) foundParentSubprojectIds;
}
//String[] visibleSubprojectIds = (String[]) subprojectModel.getPropertyValue(Subproject.XPATH_VISIBLE_SUBPROJECTS);
int estimatedSize = ((parentSubprojectIds == null) ? 0 : parentSubprojectIds.length * 2) + 1;
ArrayList<String> visibleSubprojectIdList = new ArrayList<String>(estimatedSize);
visibleSubprojectIdList.add(subprojectId);
StringBuffer visibleSubprojectIdsCsvSbuf = new StringBuffer("'"); // NB. '\'' doesn't work, counts as int !!
visibleSubprojectIdsCsvSbuf.append(subprojectId);
visibleSubprojectIdsCsvSbuf.append('\'');
if (parentSubprojectIds != null && parentSubprojectIds.length != 0) {
for (String parentSubprojectId : parentSubprojectIds) {
DocumentModel parentSubprojectModel = documentManager.getDocument(new IdRef(parentSubprojectId));
String[] parentVisibleSubprojectIds = (String[]) parentSubprojectModel.getPropertyValue(Subproject.XPATH_VISIBLE_SUBPROJECTS);
visibleSubprojectIdList.addAll(Arrays.asList(parentVisibleSubprojectIds));
String parentVisibleSubprojectIdsCsv = (String) parentSubprojectModel.getPropertyValue(Subproject.XPATH_VISIBLE_SUBPROJECTS_CSV);
// NB. should always be set, at least to itself
visibleSubprojectIdsCsvSbuf.append(',');
visibleSubprojectIdsCsvSbuf.append(parentVisibleSubprojectIdsCsv);
}
}
String[] visibleSubprojectIds = (String[]) visibleSubprojectIdList.toArray(NuxeoListUtils.EMPTY_STRING_ARRAY);
subprojectModel.setPropertyValue(Subproject.XPATH_VISIBLE_SUBPROJECTS, visibleSubprojectIds);
String visibleSubprojectIdsCsv = visibleSubprojectIdsCsvSbuf.toString();
subprojectModel.setPropertyValue(Subproject.XPATH_VISIBLE_SUBPROJECTS_CSV, visibleSubprojectIdsCsv);
// TODO only set if changed & then return true
}
/*
public static void computeAndSetVisibleSubprojects(CoreSession documentManager,
DocumentModel subprojectModel, boolean aboutToBeVersioned) throws ClientException {
String subprojectId = buildSubprojectId(subprojectModel);
subprojectModel.setPropertyValue(Subproject.XPATH_SUBPROJECT, subprojectId); // to get copied
Serializable foundParentSubprojectIds = subprojectModel.getPropertyValue(Subproject.XPATH_PARENT_SUBPROJECTS);
String[] parentSubprojectIds;
if (foundParentSubprojectIds instanceof String) {
// for easier remote initialization
parentSubprojectIds = ((String) foundParentSubprojectIds).split(",");
subprojectModel.setPropertyValue(Subproject.XPATH_PARENT_SUBPROJECTS, parentSubprojectIds);
} else {
parentSubprojectIds = (String[]) foundParentSubprojectIds;
}
int estimatedSize = ((parentSubprojectIds == null) ? 0 : parentSubprojectIds.length * 2) + 1;
ArrayList<String> visibleVersionedSubprojectIdList = new ArrayList<String>(estimatedSize);
// current subproject is necessarily live since is being modified :
ArrayList<String> visibleLiveSubprojectIdList = new ArrayList<String>(estimatedSize);
StringBuffer visibleVersionSubprojectIdsCsvSbuf = new StringBuffer("");
// current subproject is necessarily live since is being modified :
StringBuffer visibleLiveSubprojectIdsCsvSbuf = new StringBuffer("'"); // NB. '\'' doesn't work, counts as int !!
if (aboutToBeVersioned) {
visibleVersionedSubprojectIdList.add(subprojectId);
visibleVersionSubprojectIdsCsvSbuf.append(subprojectId);
visibleVersionSubprojectIdsCsvSbuf.append("',");
} else {
visibleLiveSubprojectIdList.add(subprojectId);
visibleLiveSubprojectIdsCsvSbuf.append(subprojectId);
visibleLiveSubprojectIdsCsvSbuf.append("',");
}
if (parentSubprojectIds != null && parentSubprojectIds.length != 0) {
for (String parentSubprojectId : parentSubprojectIds) {
DocumentModel parentSubprojectModel = documentManager.getDocument(new IdRef(parentSubprojectId));
String[] parentVisibleSubprojectIds = (String[]) parentSubprojectModel.getPropertyValue(Subproject.XPATH_VISIBLE_SUBPROJECTS);
visibleLiveSubprojectIdList.addAll(Arrays.asList(parentVisibleSubprojectIds));
String parentVisibleSubprojectIdsCsv = (String) parentSubprojectModel.getPropertyValue(Subproject.XPATH_VISIBLE_SUBPROJECTS_CSV);
// NB. should always be set, at least to itself
//visibleSubprojectIdsCsvSbuf.append(',');
//visibleSubprojectIdsCsvSbuf.append(parentVisibleSubprojectIdsCsv);
if (parentSubprojectModel.isVersion()) {
//visibleVersionSubprojectCriteriaSbuf.append("(ecm:isVersion=1 AND ecm:versionLabel='"
// + parentSubprojectModel.getVersionLabel() + "' AND "
// + SubprojectNode.XPATH_SUBPROJECT + "='') OR ");
visibleVersionSubprojectIdsCsvSbuf.append(parentVisibleSubprojectIdsCsv);
visibleVersionSubprojectIdsCsvSbuf.append(',');
} else {
visibleLiveSubprojectIdsCsvSbuf.append(parentVisibleSubprojectIdsCsv);
visibleLiveSubprojectIdsCsvSbuf.append(',');
}
}
}
visibleVersionSubprojectIdsCsvSbuf.deleteCharAt(visibleVersionSubprojectIdsCsvSbuf.length() - 1);
visibleLiveSubprojectIdsCsvSbuf.deleteCharAt(visibleLiveSubprojectIdsCsvSbuf.length() - 1);
//String vcsv = visibleVersionSubprojectCriteriaSbuf.toString() + " OR (ecm:isVersion AND "
// + SubprojectNode.XPATH_SUBPROJECT + " IN (" + visibleLiveSubprojectCriteriaSbuf.toString() + ")";
StringBuffer visibleVersionSubprojectCriteriaSbuf = new StringBuffer(
DocumentService.NXQL_IS_VERSIONED + DocumentService.NXQL_AND
+ SubprojectNode.XPATH_SUBPROJECT + " IN ("
+ visibleVersionSubprojectIdsCsvSbuf.toString() + ")");
StringBuffer visibleLiveSubprojectCriteriaSbuf = new StringBuffer(
DocumentService.NXQL_IS_NOT_VERSIONED + DocumentService.NXQL_AND
+ SubprojectNode.XPATH_SUBPROJECT + " IN ("
+ visibleVersionSubprojectIdsCsvSbuf.toString() + ")");
String[] visibleSubprojectIds = (String[]) visibleSubprojectIdList.toArray(NuxeoListUtils.EMPTY_STRING_ARRAY);
subprojectModel.setPropertyValue(Subproject.XPATH_VISIBLE_SUBPROJECTS, visibleSubprojectIds);
String visibleSubprojectIdsCsv = "((" + visibleVersionSubprojectCriteriaSbuf.toString()
+ ") OR (" + visibleLiveSubprojectCriteriaSbuf.toString() + "))";
subprojectModel.setPropertyValue(Subproject.XPATH_VISIBLE_SUBPROJECTS_CSV, visibleSubprojectIdsCsv);
// TODO only set if changed & then return true
}
*/
/**
* Rightens identifier after calling getSubprojectIdOrCreateDefault()
* @param documentManager
* @param identifier
* @return
* @throws ClientException
*/
public static String setDefaultSubprojectIfNone(CoreSession documentManager,
SoaNodeId identifier) throws ClientException {
String subprojectId = identifier.getSubprojectId();
if (subprojectId == null || subprojectId.length() == 0) {
// default subproject mode (TODO rather create it at startup ? or explode ?? by query or path ?)
subprojectId = (String) SubprojectServiceImpl.getOrCreateDefaultSubproject(documentManager)
.getPropertyValue(SubprojectNode.XPATH_SUBPROJECT);
identifier.setSubprojectId(subprojectId);
}
return subprojectId;
}
/**
* If possible, rather use setDefaultSubprojectIfNone()
* @param documentManager
* @param subprojectId
* @return default (live) Phase / subproject if given subprojectId is null ;
* since is live, can be queried using Path
* @throws ClientException
* @obsolete
*/
public static String getSubprojectIdOrCreateDefault(CoreSession documentManager,
String subprojectId) throws ClientException {
if (subprojectId == null || subprojectId.length() == 0) {
// default subproject mode (TODO rather create it at startup ? or explode ?? by query or path ?)
return (String) getOrCreateDefaultSubproject(documentManager)
.getPropertyValue(SubprojectNode.XPATH_SUBPROJECT);
} else {
return subprojectId;
}
}
/**
* @param documentManager
* @param subprojectId if null or no corresponding Phase / subproject, returns
* the default (live) Phase / subproject (since is live, can be queried using Path)
* @return
* @throws ClientException
*/
public static DocumentModel getSubprojectOrCreateDefault(CoreSession documentManager,
String subprojectId) throws ClientException {
DocumentModel foundSubproject = getSubprojectById(documentManager, subprojectId);
if (foundSubproject == null && (subprojectId == null || subprojectId.length() == 0
|| subprojectId.equals(Subproject.DEFAULT_SUBPROJECT_ID))) {
// default subproject mode (TODO rather create it at startup ? or explode ?? by query or path ?)
return getOrCreateDefaultSubproject(documentManager);
}
return foundSubproject;
}
/**
* Queries for it, else calls getOrCreateDefaultProject() then createSubproject().
* Returns the default (live) Phase / subproject (since is live, can be queried using Path)
* @param documentManager
* @return
* @throws ClientException
*/
private static DocumentModel getOrCreateDefaultSubproject(CoreSession documentManager) throws ClientException {
// default subproject mode (TODO rather create it at startup ? or explode ?? by query or path ?)
// querying :
// NB. since is live, could be queried using Path :
///DocumentModelList subprojectResults = documentManager.query(DocumentService.NXQL_SELECT_FROM
/// + "Subproject WHERE ecm:path='" + SubprojectServiceImpl.defaultSubprojectPathRef + "'");
// however rather querying it by id (more regular) :
DocumentModel subprojectDocModel = getSubprojectByIdInternal(documentManager, Subproject.DEFAULT_SUBPROJECT_ID);
if (subprojectDocModel == null) {
DocumentModel project = getOrCreateDefaultProject(documentManager);
subprojectDocModel = SubprojectServiceImpl.createSubproject(documentManager,
Subproject.DEFAULT_SUBPROJECT_NAME , project, null);
documentManager.save();
}
return subprojectDocModel;
}
/**
* Returns the default (live) Project (since is live, can be queried using Path)
* @param documentManager
* @return
* @throws ClientException
*/
private static DocumentModel getOrCreateDefaultProject(CoreSession documentManager) throws ClientException {
DocumentModel project;
// querying :
// NB. since is live, can be queried using Path
DocumentModelList projectResults = documentManager.query(DocumentService.NXQL_SELECT_FROM
+ "Project WHERE ecm:path='" + SubprojectServiceImpl.defaultProjectPathRef + "'");
if (projectResults == null || projectResults.isEmpty()) {
project = SubprojectServiceImpl.createProject(documentManager,
Project.DEFAULT_PROJECT_NAME);
documentManager.save();
} else {
project = projectResults.get(0);
}
return project;
}
/*
* TODO probably wrong for versions (and doesn't exclude versions if not) !!
* @obsolete rather use buildCriteriaInSubproject() which is correct
* @param subprojectId
* @return
*/
/*public static String getSubprojectById(String subprojectId) {
return DocumentService.NXQL_PATH_STARTSWITH + getPathFromId(subprojectId) + "'";
}*/
/**
*
* @param subprojectId
* @return
*/
public static String buildCriteriaInSubproject(String subprojectId) {
return SubprojectNode.XPATH_SUBPROJECT + "='" + subprojectId + "'";
}
/**
* Builds a criteria to add to a NXQL resquest to get the subproject and it's parents
* @param subprojectNode The subproject DocumentModel
* @return The criteria
* @throws PropertyException
* @throws ClientException
*/
public static String buildCriteriaSeenFromSubproject(DocumentModel subprojectNode)
throws PropertyException, ClientException {
// Filter by subproject
String implVisibleSubprojectIds = null;
if (subprojectNode.hasFacet(SubprojectNode.FACET)) {
implVisibleSubprojectIds = (String) subprojectNode
.getPropertyValue(SubprojectNode.XPATH_VISIBLE_SUBPROJECTS_CSV);
}
if (implVisibleSubprojectIds == null) {
throw new ClientException("visibleSubprojects should not be null on " + subprojectNode);
}
// ex. "AXXXSpecifications"; // or in 2 pass & get it from subProject ??
return SubprojectNode.XPATH_SUBPROJECT + " IN (" + implVisibleSubprojectIds + ")";
}
public static String buildCriteriaSeesSubproject(DocumentModel subprojectNode)
throws PropertyException, ClientException {
String subprojectId = null;
if (subprojectNode.hasFacet(SubprojectNode.FACET)) {
subprojectId = (String) subprojectNode.getPropertyValue(SubprojectNode.XPATH_SUBPROJECT);
}
if (subprojectId == null) {
throw new ClientException("subproject should not be null on " + subprojectNode);
}
///DocumentModel subproject = getSubprojectOfNode(subprojectNode);
///return SubprojectNode.XPATH_VISIBLE_SUBPROJECTS + "='" + subproject.getId() + "'"; // NB. multivalued prop
return SubprojectNode.XPATH_VISIBLE_SUBPROJECTS + "='" + subprojectId + "'"; // NB. multivalued prop
}
/**
* Uses getParent. To rather use spnode:subproject, use getSubprojectById()
* @obsolete
* @param subprojectNode
* @return
* @throws ClientException
*/
public static DocumentModel getSubprojectOfNode(DocumentModel subprojectNode) throws ClientException {
CoreSession documentManager = subprojectNode.getCoreSession();
DocumentModel parent = subprojectNode;
while (!parent.getType().equals(Subproject.DOCTYPE)) {
parent = documentManager.getDocument(parent.getParentRef());
if (parent.getType().equals("Root")) {
return null;
}
}
return parent;
}
public static String getSubprojectId(DocumentModel docModel) throws ClientException {
return (String) docModel.getPropertyValue(SubprojectNode.XPATH_SUBPROJECT);
}
public static DocumentModel createSubprojectVersion(DocumentModel subproject,
VersioningOption vOpt) throws ClientException {
long oldVersionMajor = getVersion(subproject, "major_version");//"uid:major_version"
long oldVersionMinor = getVersion(subproject, "minor_version");
String newVersionLabel;
if (vOpt == VersioningOption.MAJOR) {
newVersionLabel = (oldVersionMajor + 1) + "." + oldVersionMinor;
} else { // assuming minor
newVersionLabel = oldVersionMajor + "." + (oldVersionMinor + 1);
}
String liveSubprojectId = (String) subproject.getPropertyValue(Subproject.XPATH_SUBPROJECT);
String newVersionedSubprojectId = liveSubprojectId + newVersionLabel;
subproject.setPropertyValue(Subproject.XPATH_SUBPROJECT, newVersionedSubprojectId);
//computeAndSetVisibleSubprojects(subproject.getCoreSession(), subproject);// TODO or evented ??
///incrementVersionedSuprojectIdRecursive(subproject, newVersionLabel);// TODO or evented ??
subproject.getCoreSession().saveDocument(subproject); // triggers event that calls SubprojectNodeListener
// which will recompute subproject properties on copy them on all descendants
subproject.getCoreSession().save(); // just in case
Snapshotable snapshotable = subproject.getAdapter(Snapshotable.class);
Snapshot snapshot = snapshotable.createSnapshot(vOpt);
//documentManager.save();
subproject.setPropertyValue(Subproject.XPATH_SUBPROJECT, liveSubprojectId);
//computeAndSetVisibleSubprojects(subproject.getCoreSession(), subproject);// TODO or evented ??
///incrementVersionedSuprojectIdRecursive(subproject, newVersionLabel);// TODO or evented ??
subproject.getCoreSession().saveDocument(subproject); // triggers event that calls SubprojectNodeListener
// which will recompute subproject properties on copy them on all descendants
subproject.getCoreSession().save(); // just in case
return snapshot.getDocument();
}
/**
* TODO not used, recursive increment rather done in listener
* @param subproject
* @param newVersionLabel
* @throws PropertyException
* @throws ClientException
*/
private static void incrementVersionedSuprojectIdRecursive(DocumentModel subproject,
String newVersionLabel) throws PropertyException, ClientException {
String subprojectId = (String) subproject.getPropertyValue(Subproject.XPATH_SUBPROJECT);
String newVersionedSubprojectId = subprojectId + newVersionLabel;
subproject.setPropertyValue(Subproject.XPATH_SUBPROJECT, newVersionedSubprojectId);//TODO versioned
DocumentModelIterator childrenIt = subproject.getCoreSession().getChildrenIterator(subproject.getRef());
while (childrenIt.hasNext()) {
DocumentModel child = childrenIt.next();
incrementVersionedSuprojectIdRecursive(child, newVersionLabel);
}
}
// from StandardVersioningService TODO NUXEO OPEN IT UP
protected static long getVersion(DocumentModel doc, String prop) throws ClientException {
Object propVal = doc.getPropertyValue(prop);
if (propVal == null || !(propVal instanceof Long)) {
return 0;
} else {
return ((Long) propVal).longValue();
}
}
public static void copySubprojectNodePropertiesOnChildrenRecursive(DocumentModel subproject)
throws PropertyException, ClientException {
DocumentModelIterator childrenIt = subproject.getCoreSession().getChildrenIterator(subproject.getRef());
while (childrenIt.hasNext()) {
DocumentModel child = childrenIt.next();
if (child.isProxy()) {
logger.warn(" copySubprojectNodePropertiesOnChildrenRecursive avoiding proxy " + child);
continue;
}
copySubprojectNodeProperties(subproject, child);
child.getCoreSession().saveDocument(child); // else not persisted
copySubprojectNodePropertiesOnChildrenRecursive(child);
}
}
public static void copySubprojectNodeProperties(DocumentModel fromSubprojectNode,
DocumentModel toModel) throws PropertyException, ClientException {
DocumentModel spnode = null;
if (fromSubprojectNode != null && fromSubprojectNode.hasFacet(SubprojectNode.FACET)) {
// subproject known through parent => recopy spnode properties from it
spnode = fromSubprojectNode;
if (!toModel.hasFacet(SubprojectNode.FACET)) {
toModel.addFacet(SubprojectNode.FACET);
}
// in case of null or changing subprojectId
toModel.setPropertyValue(Subproject.XPATH_SUBPROJECT,
fromSubprojectNode.getPropertyValue(Subproject.XPATH_SUBPROJECT));
} else if (toModel.hasFacet(SubprojectNode.FACET)) { // TODO not required ?!
// subproject should be provided through property => get spnode properties from subproject
String subprojectId = (String) toModel.getPropertyValue(Subproject.XPATH_SUBPROJECT);
spnode = toModel.getCoreSession().getDocument(new IdRef(subprojectId));
} else {
logger.info("About to create document outside a subproject " + toModel);
}
if (spnode != null) {
toModel.setPropertyValue(SubprojectNode.XPATH_VISIBLE_SUBPROJECTS,
fromSubprojectNode.getPropertyValue(Subproject.XPATH_VISIBLE_SUBPROJECTS));
toModel.setPropertyValue(SubprojectNode.XPATH_VISIBLE_SUBPROJECTS_CSV,
fromSubprojectNode.getPropertyValue(Subproject.XPATH_VISIBLE_SUBPROJECTS_CSV));
// TODO or using facet inheritance through spnode:subproject ?? NOOO documentService.createDocument() ex. ITS...
}
}
/**
* Returns the parent subprojects references
* @param documentManager The Nuxeo core session
* @param subprojectId The subproject ID
* @return A String array containing the parent subprojects references
* @throws ClientException
*/
public static String[] getParentSubprojectsRef(CoreSession documentManager, String subprojectId) throws ClientException {
return getParentSubprojectsRef(getSubprojectById(documentManager, subprojectId));
}
/**
* Returns the parent subprojects references
* @param subproject The subproject DocumentModel
* @return A String array containing the parent subprojects references
* @throws ClientException
*/
public static String[] getParentSubprojectsRef(DocumentModel subproject) throws ClientException {
String[] result = (String[]) subproject.getPropertyValue(Subproject.XPATH_PARENT_SUBPROJECTS);
if(result == null){
result = new String[0];
}
return result;
}
/**
*
* @param documentManager
* @param subprojectId
* @return
* @throws ClientException
*/
public static List<DocumentModel> getParentSubprojects(CoreSession documentManager, String subprojectId) throws ClientException {
return getParentSubprojects(getSubprojectById(documentManager, subprojectId));
}
/**
*
* @param subproject
* @return
* @throws ClientException
*/
public static List<DocumentModel> getParentSubprojects(DocumentModel subproject) throws ClientException {
ArrayList<DocumentModel> result = new ArrayList<DocumentModel>();
String[] parentReferences = (String[]) subproject.getPropertyValue(Subproject.XPATH_PARENT_SUBPROJECTS);
if(parentReferences == null){
return result;
}
for(String parentRef : parentReferences){
DocumentModel parentSubprojectModel = subproject.getCoreSession().getDocument(new IdRef(parentRef));
result.add(parentSubprojectModel);
}
return result;
}
}