SHA-1629 : Creating a link to file in a different location

- Added support for multiple files in doclink.post webscript
   - Added unit test for api/node/doclink api
   - Added marker aspect app:linked for nodes that have links attached

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@131857 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Ramona Neamtu
2016-10-28 12:09:59 +00:00
parent 7457fa5b13
commit fee7ef2ad4
11 changed files with 820 additions and 614 deletions

View File

@@ -384,6 +384,7 @@
<value>alfresco.messages.authentication</value> <value>alfresco.messages.authentication</value>
<value>alfresco.messages.file-folder-service</value> <value>alfresco.messages.file-folder-service</value>
<value>alfresco.messages.custommodel-service</value> <value>alfresco.messages.custommodel-service</value>
<value>alfresco.messages.doclink-service</value>
</list> </list>
</property> </property>
</bean> </bean>

View File

@@ -61,3 +61,4 @@ org.alfresco.datalists.list-deleted={1} deleted data list {0}
org.alfresco.subscriptions.followed={1} is now following {5} org.alfresco.subscriptions.followed={1} is now following {5}
org.alfresco.subscriptions.subscribed={1} has subscribed to {2} org.alfresco.subscriptions.subscribed={1} has subscribed to {2}
org.alfresco.profile.status-changed={1}: {2} org.alfresco.profile.status-changed={1}: {2}
org.alfresco.doclink.link-created={1} created link to {0}

View File

@@ -0,0 +1,3 @@
# link service externalised display strings
doclink_service.link_to_label=Link to {0}

View File

@@ -94,11 +94,14 @@
<property name="hiddenAspect" ref="hiddenAspect"/> <property name="hiddenAspect" ref="hiddenAspect"/>
</bean> </bean>
<bean name="documentLinkService" class="org.alfresco.repo.doclink.DocumentLinkServiceImpl"> <bean name="documentLinkService" class="org.alfresco.repo.doclink.DocumentLinkServiceImpl" init-method="init">
<property name="nodeService" ref="NodeService"/> <property name="nodeService" ref="NodeService"/>
<property name="dictionaryService" ref="dictionaryService"/> <property name="dictionaryService" ref="dictionaryService"/>
<property name="searchService" ref="admSearchService"/> <property name="searchService" ref="admSearchService"/>
<property name="namespaceService" ref="namespaceService"/> <property name="namespaceService" ref="namespaceService"/>
<property name="checkOutCheckInService" ref="checkOutCheckInService"/>
<property name="policyComponent" ref="policyComponent"/>
<property name="behaviourFilter" ref="policyBehaviourFilter" />
</bean> </bean>
<bean id="mlTranslationInterceptor" class="org.alfresco.repo.model.filefolder.MLTranslationInterceptor" > <bean id="mlTranslationInterceptor" class="org.alfresco.repo.model.filefolder.MLTranslationInterceptor" >

View File

@@ -146,6 +146,10 @@
</properties> </properties>
</aspect> </aspect>
<aspect name="app:linked">
<title>Marker aspect to indicate that the node has been linked.</title>
</aspect>
</aspects> </aspects>
</model> </model>

View File

@@ -23,47 +23,50 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.model; package org.alfresco.model;
import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
/** /**
* @author Kevin Roast * @author Kevin Roast
*/ */
public interface ApplicationModel public interface ApplicationModel
{ {
// workflow // workflow
static final QName ASPECT_SIMPLE_WORKFLOW = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "simpleworkflow"); static final QName ASPECT_SIMPLE_WORKFLOW = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "simpleworkflow");
static final QName PROP_APPROVE_STEP = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "approveStep"); static final QName PROP_APPROVE_STEP = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "approveStep");
static final QName PROP_APPROVE_FOLDER = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "approveFolder"); static final QName PROP_APPROVE_FOLDER = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "approveFolder");
static final QName PROP_APPROVE_MOVE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "approveMove"); static final QName PROP_APPROVE_MOVE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "approveMove");
static final QName PROP_REJECT_STEP = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "rejectStep"); static final QName PROP_REJECT_STEP = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "rejectStep");
static final QName PROP_REJECT_FOLDER = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "rejectFolder"); static final QName PROP_REJECT_FOLDER = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "rejectFolder");
static final QName PROP_REJECT_MOVE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "rejectMove"); static final QName PROP_REJECT_MOVE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "rejectMove");
// ui facets aspect // ui facets aspect
static final QName ASPECT_UIFACETS = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "uifacets"); static final QName ASPECT_UIFACETS = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "uifacets");
static final QName PROP_ICON = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "icon"); static final QName PROP_ICON = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "icon");
// inlineeditable aspect // inlineeditable aspect
static final QName ASPECT_INLINEEDITABLE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "inlineeditable"); static final QName ASPECT_INLINEEDITABLE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "inlineeditable");
static final QName PROP_EDITINLINE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "editInline"); static final QName PROP_EDITINLINE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "editInline");
// configurable aspect // configurable aspect
static final QName ASPECT_CONFIGURABLE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "configurable"); static final QName ASPECT_CONFIGURABLE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "configurable");
static final QName TYPE_CONFIGURATIONS = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "configurations"); static final QName TYPE_CONFIGURATIONS = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "configurations");
static final QName ASSOC_CONFIGURATIONS = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "configurations"); static final QName ASSOC_CONFIGURATIONS = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "configurations");
// object links // object links
static final QName TYPE_FILELINK = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "filelink"); static final QName TYPE_FILELINK = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "filelink");
static final QName TYPE_FOLDERLINK = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "folderlink"); static final QName TYPE_FOLDERLINK = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "folderlink");
// feed source aspect // feed source aspect
static final QName ASPECT_FEEDSOURCE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "feedsource"); static final QName ASPECT_FEEDSOURCE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "feedsource");
static final QName PROP_FEEDTEMPLATE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "template"); static final QName PROP_FEEDTEMPLATE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "template");
// Default view config aspect // Default view config aspect
static final QName ASPECT_DEFAULT_VIEW_CONFIG = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "defaultViewConfig"); static final QName ASPECT_DEFAULT_VIEW_CONFIG = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "defaultViewConfig");
static final QName PROP_DEFAULT_VIEW_ID = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "defaultViewId"); static final QName PROP_DEFAULT_VIEW_ID = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "defaultViewId");
}
// Linked aspect
static final QName ASPECT_LINKED = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "linked");
}

View File

@@ -23,41 +23,43 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.repo.activities; package org.alfresco.repo.activities;
public interface ActivityType public interface ActivityType
{ {
// pre-defined alfresco activity types // pre-defined alfresco activity types
// generic fallback (if specific template is missing) // generic fallback (if specific template is missing)
public static final String GENERIC_FALLBACK = "org.alfresco.generic"; public static final String GENERIC_FALLBACK = "org.alfresco.generic";
// site membership // site membership
public static final String SITE_USER_JOINED = "org.alfresco.site.user-joined"; public static final String SITE_USER_JOINED = "org.alfresco.site.user-joined";
public static final String SITE_USER_REMOVED = "org.alfresco.site.user-left"; public static final String SITE_USER_REMOVED = "org.alfresco.site.user-left";
public static final String SITE_USER_ROLE_UPDATE = "org.alfresco.site.user-role-changed"; public static final String SITE_USER_ROLE_UPDATE = "org.alfresco.site.user-role-changed";
public static final String SITE_GROUP_ADDED = "org.alfresco.site.group-added"; public static final String SITE_GROUP_ADDED = "org.alfresco.site.group-added";
public static final String SITE_GROUP_REMOVED = "org.alfresco.site.group-removed"; public static final String SITE_GROUP_REMOVED = "org.alfresco.site.group-removed";
public static final String SITE_GROUP_ROLE_UPDATE = "org.alfresco.site.group-role-changed"; public static final String SITE_GROUP_ROLE_UPDATE = "org.alfresco.site.group-role-changed";
public static final String SUBSCRIPTIONS_SUBSCRIBE = "org.alfresco.subscriptions.subscribed"; public static final String SUBSCRIPTIONS_SUBSCRIBE = "org.alfresco.subscriptions.subscribed";
public static final String SUBSCRIPTIONS_FOLLOW = "org.alfresco.subscriptions.followed"; public static final String SUBSCRIPTIONS_FOLLOW = "org.alfresco.subscriptions.followed";
public static final String FILE_ADDED = "org.alfresco.documentlibrary.file-added"; public static final String FILE_ADDED = "org.alfresco.documentlibrary.file-added";
public static final String FILE_UPDATED = "org.alfresco.documentlibrary.file-updated"; public static final String FILE_UPDATED = "org.alfresco.documentlibrary.file-updated";
public static final String FILE_DELETED = "org.alfresco.documentlibrary.file-deleted"; public static final String FILE_DELETED = "org.alfresco.documentlibrary.file-deleted";
public static final String FILES_ADDED = "org.alfresco.documentlibrary.files-added"; public static final String FILES_ADDED = "org.alfresco.documentlibrary.files-added";
public static final String FILES_UPDATED = "org.alfresco.documentlibrary.files-updated"; public static final String FILES_UPDATED = "org.alfresco.documentlibrary.files-updated";
public static final String FILES_DELETED = "org.alfresco.documentlibrary.files-deleted"; public static final String FILES_DELETED = "org.alfresco.documentlibrary.files-deleted";
public static final String FOLDER_ADDED = "org.alfresco.documentlibrary.folder-added"; public static final String FOLDER_ADDED = "org.alfresco.documentlibrary.folder-added";
public static final String FOLDER_DELETED = "org.alfresco.documentlibrary.folder-deleted"; public static final String FOLDER_DELETED = "org.alfresco.documentlibrary.folder-deleted";
public static final String FOLDERS_ADDED = "org.alfresco.documentlibrary.folders-added"; public static final String FOLDERS_ADDED = "org.alfresco.documentlibrary.folders-added";
public static final String FOLDERS_DELETED = "org.alfresco.documentlibrary.folders-deleted"; public static final String FOLDERS_DELETED = "org.alfresco.documentlibrary.folders-deleted";
public static final String FILE_LIKED = "org.alfresco.documentlibrary.file-liked"; public static final String FILE_LIKED = "org.alfresco.documentlibrary.file-liked";
public static final String FOLDER_LIKED = "org.alfresco.documentlibrary.folder-liked"; public static final String FOLDER_LIKED = "org.alfresco.documentlibrary.folder-liked";
public static final String COMMENT_CREATED = "org.alfresco.comments.comment-created"; public static final String COMMENT_CREATED = "org.alfresco.comments.comment-created";
}
public static final String DOCLINK_CREATED = "org.alfresco.doclink.link-created";
}

View File

@@ -23,220 +23,353 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.repo.doclink; package org.alfresco.repo.doclink;
import java.io.Serializable; import java.io.Serializable;
import java.util.HashMap; import java.util.ArrayList;
import java.util.List; import java.util.HashMap;
import java.util.Map; import java.util.List;
import java.util.Map;
import org.alfresco.model.ApplicationModel;
import org.alfresco.model.ContentModel; import org.alfresco.model.ApplicationModel;
import org.alfresco.repo.model.filefolder.FileFolderServiceImpl; import org.alfresco.model.ContentModel;
import org.alfresco.repo.search.QueryParameterDefImpl; import org.alfresco.repo.node.NodeServicePolicies;
import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.repo.policy.BehaviourFilter;
import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.repo.policy.JavaBehaviour;
import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.service.cmr.repository.DeleteLinksStatusReport; import org.alfresco.repo.search.QueryParameterDefImpl;
import org.alfresco.service.cmr.repository.DocumentLinkService; import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.repo.site.SiteModel;
import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.coci.CheckOutCheckInService;
import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.search.QueryParameterDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.cmr.repository.DeleteLinksStatusReport;
import org.alfresco.service.namespace.QName; import org.alfresco.service.cmr.repository.DocumentLinkService;
import org.alfresco.util.ParameterCheck; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.util.PropertyCheck; import org.alfresco.service.cmr.repository.NodeService;
import org.apache.commons.logging.Log; import org.alfresco.service.cmr.repository.StoreRef;
import org.apache.commons.logging.LogFactory; import org.alfresco.service.cmr.search.QueryParameterDefinition;
import org.alfresco.service.cmr.search.SearchService;
/** import org.alfresco.service.namespace.NamespaceService;
* Implementation of the document link service import org.alfresco.service.namespace.QName;
* import org.alfresco.util.PropertyCheck;
* @author Ana Bozianu import org.apache.commons.logging.Log;
* @since 5.1 import org.apache.commons.logging.LogFactory;
*/ import org.springframework.extensions.surf.util.I18NUtil;
public class DocumentLinkServiceImpl implements DocumentLinkService
{ /**
private static Log logger = LogFactory.getLog(DocumentLinkServiceImpl.class); * Implementation of the document link service
* In addition to the document link service, this class also provides a BeforeDeleteNodePolicy
private NodeService nodeService; *
private DictionaryService dictionaryService; * @author Ana Bozianu
private SearchService searchService; * @since 5.1
private NamespaceService namespaceService; */
public class DocumentLinkServiceImpl implements DocumentLinkService, NodeServicePolicies.BeforeDeleteNodePolicy
/** Shallow search for nodes with a name pattern */ {
private static final String XPATH_QUERY_NODE_NAME_MATCH = "./*[like(@cm:name, $cm:name, false)]"; private static Log logger = LogFactory.getLog(DocumentLinkServiceImpl.class);
/** Shallow search for links with a destination pattern */ private NodeService nodeService;
private static final String XPATH_QUERY_LINK_DEST_MATCH = ".//*[like(@cm:destination, $cm:destination, false)]"; private DictionaryService dictionaryService;
private SearchService searchService;
@Override private NamespaceService namespaceService;
public NodeRef createDocumentLink(NodeRef source, NodeRef destination) private CheckOutCheckInService checkOutCheckInService;
{ private PolicyComponent policyComponent;
if(logger.isDebugEnabled()) private BehaviourFilter behaviourFilter;
{
logger.debug("Creating document link. source: " + source + ", destination: " + destination); /** Shallow search for nodes with a name pattern */
} private static final String XPATH_QUERY_NODE_NAME_MATCH = "./*[like(@cm:name, $cm:name, false)]";
/* Validate input */ /** Shallow search for links with a destination pattern */
PropertyCheck.mandatory(this, "source", source); private static final String XPATH_QUERY_LINK_DEST_MATCH = ".//*[like(@cm:destination, $cm:destination, false)]";
PropertyCheck.mandatory(this, "destination", destination);
private static final String LINK_NODE_EXTENSION = ".url";
// check if source node exists
if (!nodeService.exists(source)) /* I18N labels */
{ private static final String LINK_TO_LABEL = "doclink_service.link_to_label";
throw new IllegalArgumentException("Source NodeRef '" + source + "' does not exist");
} /**
// check if destination node exists * The initialise method. Register our policies.
if (!nodeService.exists(destination)) */
{ public void init()
throw new IllegalArgumentException("Destination NodeRef '" + destination + "' does not exist"); {
} PropertyCheck.mandatory(this, "nodeService", nodeService);
// check if destination node is a directory PropertyCheck.mandatory(this, "dictionaryService", dictionaryService);
if (!dictionaryService.isSubClass(nodeService.getType(destination), ContentModel.TYPE_FOLDER)) PropertyCheck.mandatory(this, "searchService", searchService);
{ PropertyCheck.mandatory(this, "namespaceService", namespaceService);
throw new IllegalArgumentException("Destination node NodeRef '" + source + "' must be of type " + ContentModel.TYPE_FOLDER); PropertyCheck.mandatory(this, "checkOutCheckInService", checkOutCheckInService);
} PropertyCheck.mandatory(this, "policyComponent", policyComponent);
PropertyCheck.mandatory(this, "behaviourFilter", behaviourFilter);
/* Create link */
String sourceName = (String) nodeService.getProperty(source, ContentModel.PROP_NAME); // Register interest in the beforeDeleteNode policy
Map<QName, Serializable> props = new HashMap<QName, Serializable>();
props.put(ContentModel.PROP_NAME, sourceName); //for nodes that have app:linked aspect
props.put(ContentModel.PROP_LINK_DESTINATION, source); policyComponent.bindClassBehaviour(
QName assocQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, QName.createValidLocalName(sourceName)); BeforeDeleteNodePolicy.QNAME,
ApplicationModel.ASPECT_LINKED,
// check if the link node already exists new JavaBehaviour(this, "beforeDeleteNode"));
if (checkExists(sourceName, destination))
{ //for app:filelink node types
throw new IllegalArgumentException("A file with the name '" + sourceName + "' already exists in the destination folder"); policyComponent.bindClassBehaviour(
} BeforeDeleteNodePolicy.QNAME,
ApplicationModel.TYPE_FILELINK,
ChildAssociationRef childRef = null; new JavaBehaviour(this, "beforeDeleteLinkNode"));
if (dictionaryService.isSubClass(nodeService.getType(source), ContentModel.TYPE_CONTENT))
{ //for app:folderlink node types
// create File Link node policyComponent.bindClassBehaviour(
childRef = nodeService.createNode(destination, ContentModel.ASSOC_CONTAINS, assocQName, ApplicationModel.TYPE_FILELINK, props); BeforeDeleteNodePolicy.QNAME,
ApplicationModel.TYPE_FOLDERLINK,
} new JavaBehaviour(this, "beforeDeleteLinkNode"));
else if (dictionaryService.isSubClass(nodeService.getType(source), ContentModel.TYPE_FOLDER)) }
{
// create Folder link node @Override
childRef = nodeService.createNode(destination, ContentModel.ASSOC_CONTAINS, assocQName, ApplicationModel.TYPE_FOLDERLINK, props); public NodeRef createDocumentLink(NodeRef source, NodeRef destination)
} {
else if(logger.isDebugEnabled())
{ {
throw new IllegalArgumentException("unsupported source node type : " + nodeService.getType(source)); logger.debug("Creating document link. source: " + source + ", destination: " + destination);
} }
return childRef.getChildRef(); /* Validate input */
} PropertyCheck.mandatory(this, "source", source);
PropertyCheck.mandatory(this, "destination", destination);
/**
* Check if node with specified <code>name</code> exists within a // check if source node exists
* <code>parent</code> folder. if (!nodeService.exists(source))
* {
* @param name throw new IllegalArgumentException("Source NodeRef '" + source + "' does not exist");
* @param parent }
* @return // check if destination node exists
*/ if (!nodeService.exists(destination))
private boolean checkExists(String name, NodeRef parent) {
{ throw new IllegalArgumentException("Destination NodeRef '" + destination + "' does not exist");
QueryParameterDefinition[] params = new QueryParameterDefinition[1]; }
params[0] = new QueryParameterDefImpl(ContentModel.PROP_NAME, dictionaryService.getDataType(DataTypeDefinition.TEXT), true, name); // check if destination node is a directory
if (!dictionaryService.isSubClass(nodeService.getType(destination), ContentModel.TYPE_FOLDER))
// execute the query {
List<NodeRef> nodeRefs = searchService.selectNodes(parent, XPATH_QUERY_NODE_NAME_MATCH, params, namespaceService, false); throw new IllegalArgumentException("Destination node NodeRef '" + source + "' must be of type " + ContentModel.TYPE_FOLDER);
}
return (nodeRefs.size() != 0); //if file is working copy - create link to the original
} if (checkOutCheckInService.isWorkingCopy(source))
{
@Override source = checkOutCheckInService.getCheckedOut(source);
public NodeRef getLinkDestination(NodeRef linkNodeRef) }
{ /* Create link */
/* Validate input */ String sourceName = (String) nodeService.getProperty(source, ContentModel.PROP_NAME);
PropertyCheck.mandatory(this, "linkNodeRef", linkNodeRef);
String newName = sourceName + LINK_NODE_EXTENSION;
/* Check if the node exists */ newName = I18NUtil.getMessage(LINK_TO_LABEL, newName);
if (!nodeService.exists(linkNodeRef))
{ Map<QName, Serializable> props = new HashMap<QName, Serializable>();
throw new IllegalArgumentException("The provided node does not exist"); props.put(ContentModel.PROP_NAME, newName);
} props.put(ContentModel.PROP_LINK_DESTINATION, source);
if (!nodeService.getType(linkNodeRef).equals(ApplicationModel.TYPE_FILELINK) QName assocQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, QName.createValidLocalName(newName));
&& !nodeService.getType(linkNodeRef).equals(ApplicationModel.TYPE_FOLDERLINK))
{ // check if the link node already exists
throw new IllegalArgumentException("The provided node is not a document link"); if (checkExists(newName, destination))
} {
throw new IllegalArgumentException("A file with the name '" + newName + "' already exists in the destination folder");
return (NodeRef) nodeService.getProperty(linkNodeRef, ContentModel.PROP_LINK_DESTINATION); }
}
ChildAssociationRef childRef = null;
@Override QName sourceType = nodeService.getType(source);
public DeleteLinksStatusReport deleteLinksToDocument(NodeRef document)
{ if (dictionaryService.isSubClass(sourceType, ContentModel.TYPE_CONTENT))
if(logger.isDebugEnabled()) {
{ // create File Link node
logger.debug("Deleting links of a document. document: " + document); childRef = nodeService.createNode(destination, ContentModel.ASSOC_CONTAINS, assocQName, ApplicationModel.TYPE_FILELINK, props);
}
}
/* Validate input */ else if (!dictionaryService.isSubClass(sourceType, SiteModel.TYPE_SITE) && dictionaryService.isSubClass(nodeService.getType(source), ContentModel.TYPE_FOLDER))
PropertyCheck.mandatory(this, "document", document); {
// create Folder link node
/* Get all links of the given document */ childRef = nodeService.createNode(destination, ContentModel.ASSOC_CONTAINS, assocQName, ApplicationModel.TYPE_FOLDERLINK, props);
QueryParameterDefinition[] params = new QueryParameterDefinition[1]; }
params[0] = new QueryParameterDefImpl(ContentModel.PROP_LINK_DESTINATION, dictionaryService.getDataType(DataTypeDefinition.NODE_REF), true, document.toString()); else
{
/* Search for links in all stores */ throw new IllegalArgumentException("unsupported source node type : " + nodeService.getType(source));
DeleteLinksStatusReport report = new DeleteLinksStatusReport(); }
for(StoreRef store : nodeService.getStores())
{ //add linked aspect to the sourceNode
/* Get the root node */ nodeService.addAspect(source, ApplicationModel.ASPECT_LINKED, null);
NodeRef rootNodeRef = nodeService.getRootNode(store);
return childRef.getChildRef();
/* Execute the query, retrieve links to the document*/ }
List<NodeRef> nodeRefs = searchService.selectNodes(rootNodeRef, XPATH_QUERY_LINK_DEST_MATCH, params, namespaceService, true);
report.addTotalLinksFoundCount(nodeRefs.size()); /**
* Check if node with specified <code>name</code> exists within a
/* Delete the found nodes */ * <code>parent</code> folder.
for(NodeRef linkRef : nodeRefs) *
{ * @param name
try{ * @param parent
nodeService.deleteNode(linkRef); * @return
*/
/* if the node was successfully deleted increment the count */ private boolean checkExists(String name, NodeRef parent)
report.incrementDeletedLinksCount(); {
} QueryParameterDefinition[] params = new QueryParameterDefinition[1];
catch(AccessDeniedException ex) params[0] = new QueryParameterDefImpl(ContentModel.PROP_NAME, dictionaryService.getDataType(DataTypeDefinition.TEXT), true, name);
{
/* if the node could not be deleted add it to the report */ // execute the query
report.addErrorDetail(linkRef, ex); List<NodeRef> nodeRefs = searchService.selectNodes(parent, XPATH_QUERY_NODE_NAME_MATCH, params, namespaceService, false);
}
} return (nodeRefs.size() != 0);
} }
return report; @Override
} public NodeRef getLinkDestination(NodeRef linkNodeRef)
{
public void setNodeService(NodeService nodeService) /* Validate input */
{ PropertyCheck.mandatory(this, "linkNodeRef", linkNodeRef);
this.nodeService = nodeService;
} /* Check if the node exists */
if (!nodeService.exists(linkNodeRef))
public void setDictionaryService(DictionaryService dictionaryService) {
{ throw new IllegalArgumentException("The provided node does not exist");
this.dictionaryService = dictionaryService; }
}
if (!nodeService.getType(linkNodeRef).equals(ApplicationModel.TYPE_FILELINK)
public void setSearchService(SearchService searchService) && !nodeService.getType(linkNodeRef).equals(ApplicationModel.TYPE_FOLDERLINK))
{ {
this.searchService = searchService; throw new IllegalArgumentException("The provided node is not a document link");
} }
public void setNamespaceService(NamespaceService namespaceService) return (NodeRef) nodeService.getProperty(linkNodeRef, ContentModel.PROP_LINK_DESTINATION);
{ }
this.namespaceService = namespaceService;
} @Override
} public List<NodeRef> getNodeLinks(NodeRef nodeRef)
{
/* Validate input */
PropertyCheck.mandatory(this, "nodeRef", nodeRef);
/* Get all links of the given nodeRef */
QueryParameterDefinition[] params = new QueryParameterDefinition[1];
params[0] = new QueryParameterDefImpl(ContentModel.PROP_LINK_DESTINATION, dictionaryService.getDataType(DataTypeDefinition.NODE_REF), true, nodeRef.toString());
List<NodeRef> nodeLinks = new ArrayList<NodeRef>();
List<NodeRef> nodeRefs;
/* Search for links in all stores */
for(StoreRef store : nodeService.getStores())
{
/* Get the root node */
NodeRef rootNodeRef = nodeService.getRootNode(store);
/* Execute the query, retrieve links to the document*/
nodeRefs = searchService.selectNodes(rootNodeRef, XPATH_QUERY_LINK_DEST_MATCH, params, namespaceService, true);
nodeLinks.addAll(nodeRefs);
}
return nodeLinks;
}
@Override
public DeleteLinksStatusReport deleteLinksToDocument(NodeRef document)
{
if (logger.isDebugEnabled())
{
logger.debug("Deleting links of a document. document: " + document);
}
/* Validate input */
PropertyCheck.mandatory(this, "document", document);
DeleteLinksStatusReport report = new DeleteLinksStatusReport();
List<NodeRef> linkNodeRefs = getNodeLinks(document);
report.addTotalLinksFoundCount(linkNodeRefs.size());
for (NodeRef linkRef : linkNodeRefs)
{
try
{
nodeService.deleteNode(linkRef);
/* if the node was successfully deleted increment the count */
report.incrementDeletedLinksCount();
}
catch (AccessDeniedException ex)
{
/* if the node could not be deleted add it to the report */
report.addErrorDetail(linkRef, ex);
}
}
// remove also the aspect app:linked
nodeService.removeAspect(document, ApplicationModel.ASPECT_LINKED);
return report;
}
@Override
public void beforeDeleteNode(NodeRef nodeRef)
{
behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
try
{
deleteLinksToDocument(nodeRef);
}
finally
{
behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
}
}
public void beforeDeleteLinkNode(NodeRef linkNodeRef)
{
// NodeRef linkNodeRef = childAssocRef.getChildRef();
NodeRef nodeRef = getLinkDestination(linkNodeRef);
List<NodeRef> nodeRefLinks = getNodeLinks(nodeRef);
if (nodeRefLinks.size() == 1 && nodeRefLinks.contains(linkNodeRef))
{
behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
try
{
// remove linked aspect to the sourceNode
nodeService.removeAspect(nodeRef, ApplicationModel.ASPECT_LINKED);
}
finally
{
behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
}
}
}
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public void setDictionaryService(DictionaryService dictionaryService)
{
this.dictionaryService = dictionaryService;
}
public void setSearchService(SearchService searchService)
{
this.searchService = searchService;
}
public void setNamespaceService(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
}
public void setCheckOutCheckInService(CheckOutCheckInService checkOutCheckInService)
{
this.checkOutCheckInService = checkOutCheckInService;
}
public void setPolicyComponent(PolicyComponent policyComponent)
{
this.policyComponent = policyComponent;
}
public void setBehaviourFilter(BehaviourFilter behaviourFilter)
{
this.behaviourFilter = behaviourFilter;
}
}

View File

@@ -23,8 +23,10 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.service.cmr.repository; package org.alfresco.service.cmr.repository;
import java.util.List;
/** /**
* Provides methods specific to manipulating links of documents * Provides methods specific to manipulating links of documents
* *
@@ -52,7 +54,15 @@ public interface DocumentLinkService
* The link node. * The link node.
* @return A reference to the destination of the provided link node * @return A reference to the destination of the provided link node
*/ */
public NodeRef getLinkDestination(NodeRef linkNodeRef); public NodeRef getLinkDestination(NodeRef linkNodeRef);
/**
* Returns the associated links for a node, from all stores
*
* @param nodeRef
* @return A list of link nodeRefs for given node
*/
public List<NodeRef> getNodeLinks(NodeRef nodeRef);
/** /**
* Deletes all links having the provided node as destination. * Deletes all links having the provided node as destination.

View File

@@ -0,0 +1,43 @@
/*
* #%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;
import junit.framework.Test;
import junit.framework.TestSuite;
/**
* All Repository project UNIT test classes should be added to this test suite.
*/
public class Repository67TestSuite extends TestSuite
{
public static Test suite()
{
TestSuite suite = new TestSuite();
Repository01TestSuite.tests67(suite);
return suite;
}
}

View File

@@ -23,314 +23,317 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.repo.doclink; package org.alfresco.repo.doclink;
import java.io.Serializable; import java.io.Serializable;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javax.transaction.Status; import javax.transaction.Status;
import javax.transaction.UserTransaction; import javax.transaction.UserTransaction;
import junit.framework.TestCase; import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ApplicationModel;
import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel;
import org.alfresco.model.ApplicationModel; import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.model.ContentModel; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.service.ServiceRegistry;
import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.repository.DeleteLinksStatusReport;
import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.repository.DocumentLinkService;
import org.alfresco.service.cmr.repository.DeleteLinksStatusReport; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.DocumentLinkService; import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.site.SiteService;
import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.site.SiteVisibility;
import org.alfresco.service.cmr.site.SiteService; import org.alfresco.service.namespace.QName;
import org.alfresco.service.cmr.site.SiteVisibility; import org.alfresco.service.transaction.TransactionService;
import org.alfresco.service.namespace.QName; import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.GUID;
import org.alfresco.util.ApplicationContextHelper; import org.springframework.context.ApplicationContext;
import org.alfresco.util.GUID; import org.springframework.extensions.surf.util.I18NUtil;
import org.springframework.context.ApplicationContext;
import junit.framework.TestCase;
/**
* Test cases for {@link DocumentLinkServiceImpl}. /**
* * Test cases for {@link DocumentLinkServiceImpl}.
* @author Ana Bozianu *
* @since 5.1 * @author Ana Bozianu
*/ * @since 5.1
*/
public class DocumentLinkServiceImplTest extends TestCase
{ public class DocumentLinkServiceImplTest extends TestCase
{
private static final ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
private static final String TEST_USER = DocumentLinkServiceImplTest.class.getSimpleName() + "_testuser"; private static final ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
private static final String TEST_USER = DocumentLinkServiceImplTest.class.getSimpleName() + "_testuser";
private UserTransaction txn;
private UserTransaction txn;
private TransactionService transactionService;
private DocumentLinkService documentLinkService; private TransactionService transactionService;
private PermissionService permissionService; private DocumentLinkService documentLinkService;
private PersonService personService; private PermissionService permissionService;
private SiteService siteService; private PersonService personService;
private FileFolderService fileFolderService; private SiteService siteService;
private NodeService nodeService; private FileFolderService fileFolderService;
private NodeService nodeService;
// nodes the test user has read/write permission to
private NodeRef site1File1; // nodes the test user has read/write permission to
private NodeRef site1File2; // do not create links of this file private NodeRef site1File1;
private NodeRef site1Folder1; private NodeRef site1File2; // do not create links of this file
private NodeRef site1Folder2; private NodeRef site1Folder1;
private NodeRef site1Folder3; private NodeRef site1Folder2;
private NodeRef site1Folder3;
// nodes the test user has no permission to
private NodeRef site2File; // nodes the test user has no permission to
private NodeRef site2Folder1; private NodeRef site2File;
private NodeRef site2Folder2; private NodeRef site2Folder1;
private NodeRef linkOfFile1Site2; private NodeRef site2Folder2;
private NodeRef linkOfFile1Site2;
private String site1File1Name = GUID.generate();
private String site1Folder1Name = GUID.generate();; private String site1File1Name = GUID.generate();
private String site1Folder1Name = GUID.generate();;
@Override
public void setUp() throws Exception @Override
{ public void setUp() throws Exception
// Set up the services {
ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean("ServiceRegistry"); // Set up the services
transactionService = serviceRegistry.getTransactionService(); ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean("ServiceRegistry");
documentLinkService = serviceRegistry.getDocumentLinkService(); transactionService = serviceRegistry.getTransactionService();
permissionService = serviceRegistry.getPermissionService(); documentLinkService = serviceRegistry.getDocumentLinkService();
personService = serviceRegistry.getPersonService(); permissionService = serviceRegistry.getPermissionService();
siteService = serviceRegistry.getSiteService(); personService = serviceRegistry.getPersonService();
fileFolderService = serviceRegistry.getFileFolderService(); siteService = serviceRegistry.getSiteService();
nodeService = serviceRegistry.getNodeService(); fileFolderService = serviceRegistry.getFileFolderService();
nodeService = serviceRegistry.getNodeService();
// Start the transaction
txn = transactionService.getUserTransaction(); // Start the transaction
txn.begin(); txn = transactionService.getUserTransaction();
txn.begin();
// Authenticate
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); // Authenticate
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
/* Create the test user */
Map<QName, Serializable> props = new HashMap<QName, Serializable>(); /* Create the test user */
props.put(ContentModel.PROP_USERNAME, TEST_USER); Map<QName, Serializable> props = new HashMap<QName, Serializable>();
personService.createPerson(props); props.put(ContentModel.PROP_USERNAME, TEST_USER);
personService.createPerson(props);
/*
* Create the working test root 1 to which the user has read/write /*
* permission * Create the working test root 1 to which the user has read/write
*/ * permission
NodeRef site1 = siteService.createSite("site1", GUID.generate(), "myTitle", "myDescription", SiteVisibility.PUBLIC).getNodeRef(); */
permissionService.setPermission(site1, TEST_USER, PermissionService.ALL_PERMISSIONS, true); NodeRef site1 = siteService.createSite("site1", GUID.generate(), "myTitle", "myDescription", SiteVisibility.PUBLIC).getNodeRef();
site1Folder1 = fileFolderService.create(site1, site1Folder1Name, ContentModel.TYPE_FOLDER).getNodeRef(); permissionService.setPermission(site1, TEST_USER, PermissionService.ALL_PERMISSIONS, true);
site1File1 = fileFolderService.create(site1Folder1, site1File1Name, ContentModel.TYPE_CONTENT).getNodeRef(); site1Folder1 = fileFolderService.create(site1, site1Folder1Name, ContentModel.TYPE_FOLDER).getNodeRef();
site1File2 = fileFolderService.create(site1Folder1, GUID.generate(), ContentModel.TYPE_CONTENT).getNodeRef(); site1File1 = fileFolderService.create(site1Folder1, site1File1Name, ContentModel.TYPE_CONTENT).getNodeRef();
site1Folder2 = fileFolderService.create(site1, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef(); site1File2 = fileFolderService.create(site1Folder1, GUID.generate(), ContentModel.TYPE_CONTENT).getNodeRef();
site1Folder3 = fileFolderService.create(site1, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef(); site1Folder2 = fileFolderService.create(site1, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
// create a link of site1File1 in site1Folder3 to test regular deletion site1Folder3 = fileFolderService.create(site1, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
documentLinkService.createDocumentLink(site1File2, site1Folder3); // create a link of site1File1 in site1Folder3 to test regular deletion
documentLinkService.createDocumentLink(site1File2, site1Folder3);
/* Create the working test root 2 to which the user has no permission */
NodeRef site2 = siteService.createSite("site2", GUID.generate(), "myTitle", "myDescription", SiteVisibility.PRIVATE).getNodeRef(); /* Create the working test root 2 to which the user has no permission */
permissionService.setPermission(site2, TEST_USER, PermissionService.ALL_PERMISSIONS, false); NodeRef site2 = siteService.createSite("site2", GUID.generate(), "myTitle", "myDescription", SiteVisibility.PRIVATE).getNodeRef();
site2File = fileFolderService.create(site2, GUID.generate(), ContentModel.TYPE_CONTENT).getNodeRef(); permissionService.setPermission(site2, TEST_USER, PermissionService.ALL_PERMISSIONS, false);
site2Folder1 = fileFolderService.create(site2, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef(); site2File = fileFolderService.create(site2, GUID.generate(), ContentModel.TYPE_CONTENT).getNodeRef();
site2Folder2 = fileFolderService.create(site2, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef(); site2Folder1 = fileFolderService.create(site2, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
// Create a link of site1File1 in site2Folder2 to test the deletion site2Folder2 = fileFolderService.create(site2, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
// without permission // Create a link of site1File1 in site2Folder2 to test the deletion
linkOfFile1Site2 = documentLinkService.createDocumentLink(site1File2, site2Folder2); // without permission
} linkOfFile1Site2 = documentLinkService.createDocumentLink(site1File2, site2Folder2);
}
@Override
protected void tearDown() throws Exception @Override
{ protected void tearDown() throws Exception
try {
{ try
if (txn.getStatus() != Status.STATUS_ROLLEDBACK && txn.getStatus() != Status.STATUS_COMMITTED) {
{ if (txn.getStatus() != Status.STATUS_ROLLEDBACK && txn.getStatus() != Status.STATUS_COMMITTED)
txn.rollback(); {
} txn.rollback();
} }
catch (Throwable e) }
{ catch (Throwable e)
e.printStackTrace(); {
} e.printStackTrace();
} }
}
/**
* Tests the creation of a file link /**
* * Tests the creation of a file link
* @throws Exception *
*/ * @throws Exception
public void testCreateFileLink() throws Exception */
{ public void testCreateFileLink() throws Exception
// create a link of file site1File1_1 in folder site1Folder2 {
NodeRef linkNodeRef = documentLinkService.createDocumentLink(site1File1, site1Folder2); // create a link of file site1File1_1 in folder site1Folder2
assertNotNull(linkNodeRef); NodeRef linkNodeRef = documentLinkService.createDocumentLink(site1File1, site1Folder2);
assertNotNull(linkNodeRef);
// test if the link node is listed as a child of site1Folder2
NodeRef linkNodeRef2 = fileFolderService.searchSimple(site1Folder2, site1File1Name); // test if the link node is listed as a child of site1Folder2
assertNotNull(linkNodeRef2); String site1File1LinkName = I18NUtil.getMessage("doclink_service.link_to_label", (site1File1Name + ".url"));
assertEquals(linkNodeRef, linkNodeRef2); NodeRef linkNodeRef2 = fileFolderService.searchSimple(site1Folder2, site1File1LinkName);
assertNotNull(linkNodeRef2);
// check the type of the link assertEquals(linkNodeRef, linkNodeRef2);
assertEquals(nodeService.getType(linkNodeRef), ApplicationModel.TYPE_FILELINK);
// check the type of the link
// check if the link destination is site1File1_1 assertEquals(nodeService.getType(linkNodeRef), ApplicationModel.TYPE_FILELINK);
NodeRef linkDestination = documentLinkService.getLinkDestination(linkNodeRef);
assertNotNull(linkDestination); // check if the link destination is site1File1_1
assertEquals(linkDestination, site1File1); NodeRef linkDestination = documentLinkService.getLinkDestination(linkNodeRef);
} assertNotNull(linkDestination);
assertEquals(linkDestination, site1File1);
/** }
* Tests the creation of a folder link
* /**
* @throws Exception * Tests the creation of a folder link
*/ *
public void testCreateFolderLink() throws Exception * @throws Exception
{ */
// create a link of file site1File1_1 in folder site1Folder2 public void testCreateFolderLink() throws Exception
NodeRef linkNodeRef = documentLinkService.createDocumentLink(site1Folder1, site1Folder2); {
assertNotNull(linkNodeRef); // create a link of file site1File1_1 in folder site1Folder2
NodeRef linkNodeRef = documentLinkService.createDocumentLink(site1Folder1, site1Folder2);
// test if the link node is listed as a child of site1Folder2 assertNotNull(linkNodeRef);
NodeRef linkNodeRef2 = fileFolderService.searchSimple(site1Folder2, site1Folder1Name);
assertNotNull(linkNodeRef2); // test if the link node is listed as a child of site1Folder2
assertEquals(linkNodeRef, linkNodeRef2); String site1Folder1LinkName = I18NUtil.getMessage("doclink_service.link_to_label", (site1Folder1Name + ".url"));
NodeRef linkNodeRef2 = fileFolderService.searchSimple(site1Folder2, site1Folder1LinkName);
// check the type of the link assertNotNull(linkNodeRef2);
assertEquals(nodeService.getType(linkNodeRef), ApplicationModel.TYPE_FOLDERLINK); assertEquals(linkNodeRef, linkNodeRef2);
// check if the link destination is site1File1_1 // check the type of the link
NodeRef linkDestination = documentLinkService.getLinkDestination(linkNodeRef); assertEquals(nodeService.getType(linkNodeRef), ApplicationModel.TYPE_FOLDERLINK);
assertNotNull(linkDestination);
assertEquals(linkDestination, site1Folder1); // check if the link destination is site1File1_1
} NodeRef linkDestination = documentLinkService.getLinkDestination(linkNodeRef);
assertNotNull(linkDestination);
/** assertEquals(linkDestination, site1Folder1);
* Tests the behavior of createDocumentLink when providing null arguments }
*
* @throws Exception /**
*/ * Tests the behavior of createDocumentLink when providing null arguments
public void testCreateDocumentWithNullArguments() throws Exception *
{ * @throws Exception
try */
{ public void testCreateDocumentWithNullArguments() throws Exception
documentLinkService.createDocumentLink(null, null); {
fail("null arguments must generate AlfrescoRuntimeException."); try
} {
catch (AlfrescoRuntimeException e) documentLinkService.createDocumentLink(null, null);
{ fail("null arguments must generate AlfrescoRuntimeException.");
// Expected }
} catch (AlfrescoRuntimeException e)
} {
// Expected
/** }
* Test the behavior of createDocumentLink when provided a file as a }
* destination
* /**
* @throws Exception * Test the behavior of createDocumentLink when provided a file as a
*/ * destination
public void testInvalidDestination() throws Exception *
{ * @throws Exception
try */
{ public void testInvalidDestination() throws Exception
documentLinkService.createDocumentLink(site1Folder2, site1File1); {
fail("invalid destination argument must generate IllegalArgumentException."); try
} {
catch (IllegalArgumentException e) documentLinkService.createDocumentLink(site1Folder2, site1File1);
{ fail("invalid destination argument must generate IllegalArgumentException.");
// Expected }
} catch (IllegalArgumentException e)
} {
// Expected
/** }
* Tests the creation of a document link when the user doesn't have read }
* permission on the source node
* /**
* @throws Exception * Tests the creation of a document link when the user doesn't have read
*/ * permission on the source node
public void testCreateDocLinkWithoutReadPermissionOnSource() throws Exception *
{ * @throws Exception
try */
{ public void testCreateDocLinkWithoutReadPermissionOnSource() throws Exception
AuthenticationUtil.runAs(new RunAsWork<Void>() {
{ try
@Override {
public Void doWork() throws Exception AuthenticationUtil.runAs(new RunAsWork<Void>()
{ {
documentLinkService.createDocumentLink(site2File, site1Folder2); @Override
return null; public Void doWork() throws Exception
} {
}, TEST_USER); documentLinkService.createDocumentLink(site2File, site1Folder2);
return null;
fail("no read permission on the source node must generate AccessDeniedException."); }
} }, TEST_USER);
catch (AccessDeniedException ex)
{ fail("no read permission on the source node must generate AccessDeniedException.");
// Expected }
} catch (AccessDeniedException ex)
} {
// Expected
/** }
* Tests the creation of a document link when the user doesn't have write }
* permission on the destination node
* /**
* @throws Exception * Tests the creation of a document link when the user doesn't have write
*/ * permission on the destination node
public void testCreateDocLinkWithoutWritePermissionOnDestination() throws Exception *
{ * @throws Exception
try */
{ public void testCreateDocLinkWithoutWritePermissionOnDestination() throws Exception
AuthenticationUtil.runAs(new RunAsWork<Void>() {
{ try
@Override {
public Void doWork() throws Exception AuthenticationUtil.runAs(new RunAsWork<Void>()
{ {
documentLinkService.createDocumentLink(site1File1, site2Folder1); @Override
return null; public Void doWork() throws Exception
} {
}, TEST_USER); documentLinkService.createDocumentLink(site1File1, site2Folder1);
return null;
fail("no write permission on the destination node must generate AccessDeniedException."); }
} }, TEST_USER);
catch (AccessDeniedException ex)
{ fail("no write permission on the destination node must generate AccessDeniedException.");
// Expected }
} catch (AccessDeniedException ex)
} {
// Expected
/** }
* Tests the deletion of a document's links, with and without write }
* permissions
* /**
* @throws Exception * Tests the deletion of a document's links, with and without write
*/ * permissions
public void testDeleteLinks() throws Exception *
{ * @throws Exception
DeleteLinksStatusReport report = AuthenticationUtil.runAs(new RunAsWork<DeleteLinksStatusReport>() */
{ public void testDeleteLinks() throws Exception
@Override {
public DeleteLinksStatusReport doWork() throws Exception DeleteLinksStatusReport report = AuthenticationUtil.runAs(new RunAsWork<DeleteLinksStatusReport>()
{ {
return documentLinkService.deleteLinksToDocument(site1File2); @Override
} public DeleteLinksStatusReport doWork() throws Exception
}, TEST_USER); {
return documentLinkService.deleteLinksToDocument(site1File2);
// check if the service found 2 links of the document }
assertEquals(report.getTotalLinksFoundCount(), 2); }, TEST_USER);
// check if the service successfully deleted one // check if the service found 2 links of the document
assertEquals(report.getDeletedLinksCount(), 1); assertEquals(report.getTotalLinksFoundCount(), 2);
// check if the second one failed with access denied // check if the service successfully deleted one
Throwable ex = report.getErrorDetails().get(linkOfFile1Site2); assertEquals(report.getDeletedLinksCount(), 1);
assertNotNull(ex);
assertEquals(ex.getClass(), AccessDeniedException.class); // check if the second one failed with access denied
} Throwable ex = report.getErrorDetails().get(linkOfFile1Site2);
assertNotNull(ex);
} assertEquals(ex.getClass(), AccessDeniedException.class);
}
}