diff --git a/config/alfresco/application-context.xml b/config/alfresco/application-context.xml index 3fb6faaaa2..adc5ffc7ac 100644 --- a/config/alfresco/application-context.xml +++ b/config/alfresco/application-context.xml @@ -45,6 +45,7 @@ + diff --git a/config/alfresco/model/contentModel.xml b/config/alfresco/model/contentModel.xml index b21722edf3..b14b07b45c 100644 --- a/config/alfresco/model/contentModel.xml +++ b/config/alfresco/model/contentModel.xml @@ -762,6 +762,17 @@ + + + Tag Scope + + + Tags + d:content + true + + + Attachable diff --git a/config/alfresco/tagging-services-context.xml b/config/alfresco/tagging-services-context.xml new file mode 100644 index 0000000000..d5f1e8a715 --- /dev/null +++ b/config/alfresco/tagging-services-context.xml @@ -0,0 +1,53 @@ + + + + + + + + org.alfresco.service.cmr.tagging.TaggingService + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + + + + + + + + + + + false + + + + + + + diff --git a/source/java/org/alfresco/model/ContentModel.java b/source/java/org/alfresco/model/ContentModel.java index b869116ef4..18cc85e8d9 100644 --- a/source/java/org/alfresco/model/ContentModel.java +++ b/source/java/org/alfresco/model/ContentModel.java @@ -142,6 +142,10 @@ public interface ContentModel static final QName ASPECT_TAGGABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "taggable"); static final QName PROP_TAGS = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "taggable"); + // tagscope aspect + static final QName ASPECT_TAGSCOPE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "tagscope"); + static final QName PROP_TAGSCOPE_CACHE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "tagScopeCache"); + // lock aspect public final static QName ASPECT_LOCKABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "lockable"); public final static QName PROP_LOCK_OWNER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "lockOwner"); @@ -275,6 +279,6 @@ public interface ContentModel static final QName TYPE_AUTHORITY_CONTAINER = QName.createQName(USER_MODEL_URI, "authorityContainer"); static final QName PROP_AUTHORITY_NAME = QName.createQName(USER_MODEL_URI, "authorityName"); static final QName ASSOC_MEMBER = QName.createQName(USER_MODEL_URI, "member"); - static final QName PROP_MEMBERS = QName.createQName(USER_MODEL_URI, "members"); + static final QName PROP_MEMBERS = QName.createQName(USER_MODEL_URI, "members"); } diff --git a/source/java/org/alfresco/repo/preference/PreferenceServiceImplTest.java b/source/java/org/alfresco/repo/preference/PreferenceServiceImplTest.java index 5570f0fb84..0c37e2c139 100644 --- a/source/java/org/alfresco/repo/preference/PreferenceServiceImplTest.java +++ b/source/java/org/alfresco/repo/preference/PreferenceServiceImplTest.java @@ -101,7 +101,7 @@ public class PreferenceServiceImplTest extends BaseAlfrescoSpringTest prefs.put("atTheRoot", "thisIsAtTheRoot"); this.preferenceService.setPreferences(USER_ONE, prefs); - // assertEquals(USER_ONE, AuthenticationUtil.getCurrentUserName()); + //assertEquals(USER_ONE, AuthenticationUtil.getCurrentUserName()); NodeRef personNodeRef = this.personService.getPerson(USER_ONE); ContentReader reader = this.contentService.getReader(personNodeRef, ContentModel.PROP_PREFERENCE_VALUES); @@ -133,13 +133,13 @@ public class PreferenceServiceImplTest extends BaseAlfrescoSpringTest assertNotNull(prefs); assertEquals(0, prefs.size()); - // assertEquals(USER_ONE, AuthenticationUtil.getCurrentUserName()); + //assertEquals(USER_ONE, AuthenticationUtil.getCurrentUserName()); } public void xtestBadUser() { - //assertEquals(USER_ONE, authenticationComponent.getCurrentUserName()); + assertEquals(USER_ONE, authenticationComponent.getCurrentUserName()); try { diff --git a/source/java/org/alfresco/repo/site/SiteService.java b/source/java/org/alfresco/repo/site/SiteService.java index 774a7a5d23..3022c5f652 100644 --- a/source/java/org/alfresco/repo/site/SiteService.java +++ b/source/java/org/alfresco/repo/site/SiteService.java @@ -3,6 +3,7 @@ package org.alfresco.repo.site; import java.util.List; import java.util.Map; +import org.alfresco.service.Auditable; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; @@ -25,7 +26,6 @@ public interface SiteService * @param isPublic whether the site is public or not * @return SiteInfo information about the created site */ - // TODO ... audit information SiteInfo createSite(String sitePreset, String shortName, String title, String description, boolean isPublic); /** @@ -35,7 +35,6 @@ public interface SiteService * @param sitePresetFilter site preset filter * @return List list of site information */ - // TODO audit information List listSites(String nameFilter, String sitePresetFilter); /** diff --git a/source/java/org/alfresco/repo/tagging/TagDetailsImpl.java b/source/java/org/alfresco/repo/tagging/TagDetailsImpl.java new file mode 100644 index 0000000000..64bbe56240 --- /dev/null +++ b/source/java/org/alfresco/repo/tagging/TagDetailsImpl.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2005-2008 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.tagging; + +import org.alfresco.service.cmr.tagging.TagDetails; + +/** + * Contains the details of a tag within a specific tag scope. + * + * @author Roy Wetherall + */ +public class TagDetailsImpl implements TagDetails +{ + /** Tag name */ + private String tagName; + + /** Tag count */ + private int tagCount; + + /** + * Constructor + * + * @param tagScope tag scope + * @param tagName tag name + * @param tagCount tag count + */ + /*package*/ TagDetailsImpl(String tagName, int tagCount) + { + this.tagName = tagName; + this.tagCount = tagCount; + } + + /** + * @see org.alfresco.service.cmr.tagging.TagDetails#getTagName() + */ + public String getTagName() + { + return this.tagName; + } + + /** + * @see org.alfresco.service.cmr.tagging.TagDetails#getTagCount() + */ + public int getTagCount() + { + return this.tagCount; + } + + /** + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() + { + return this.tagName.hashCode(); + } + + /** + * Increment the tag count. + */ + /*protected*/ void incrementCount() + { + this.tagCount = this.tagCount + 1; + } + + /** + * Decrement the tag count + */ + /*protected*/ void decrementCount() + { + this.tagCount = tagCount - 1; + } + + /** + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj instanceof TagDetailsImpl) + { + TagDetailsImpl that = (TagDetailsImpl) obj; + return (this.tagName.equals(that.tagName)); + } + else + { + return false; + } + } + + /** + * @see java.lang.Comparable#compareTo(java.lang.Object) + */ + public int compareTo(TagDetails o) + { + int result = 0; + if (this.tagCount < o.getTagCount()) + { + result = 1; + } + else if (this.tagCount > o.getTagCount()) + { + result = -1; + } + return result; + } +} diff --git a/source/java/org/alfresco/repo/tagging/TagScopeImpl.java b/source/java/org/alfresco/repo/tagging/TagScopeImpl.java new file mode 100644 index 0000000000..512a57572e --- /dev/null +++ b/source/java/org/alfresco/repo/tagging/TagScopeImpl.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2005-2008 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.tagging; + +import java.util.List; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.tagging.TagDetails; +import org.alfresco.service.cmr.tagging.TagScope; + +/** + * Tag Scope. + * + * Represents the roll up of tags within the scope of a node tree. + * + * @author Roy Wetherall + */ +public class TagScopeImpl implements TagScope +{ + /** Node reference of node that has the tag scope aspect applied */ + private NodeRef nodeRef; + + /** Ordered list of tag details */ + private List tagDetails; + + /** + * Constructor + * + * @param nodeRef node reference + */ + /*package*/ TagScopeImpl(NodeRef nodeRef, List tagDetails) + { + this.nodeRef = nodeRef; + this.tagDetails = tagDetails; + } + + /** + * Get the node reference of the tag scope + * + * @return node reference of the tag scope + */ + public NodeRef getNodeRef() + { + return this.nodeRef; + } + + /** + * @see org.alfresco.service.cmr.tagging.TagScope#getTags() + */ + public List getTags() + { + return this.tagDetails; + } + + /** + * @see org.alfresco.service.cmr.tagging.TagScope#getTags(int) + */ + public List getTags(int topN) + { + return this.tagDetails.subList(0, topN); + } + + /** + * @see org.alfresco.service.cmr.tagging.TagScope#getTag(java.lang.String) + */ + public TagDetails getTag(String tag) + { + TagDetails result = null; + for (TagDetails tagDetails : this.tagDetails) + { + if (tagDetails.getTagName().equals(tag) == true) + { + result = tagDetails; + break; + } + } + return result; + } + + /** + * @see org.alfresco.service.cmr.tagging.TagScope#isTagInScope(java.lang.String) + */ + public boolean isTagInScope(String tag) + { + return (getTag(tag) != null); + } + + /** + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() + { + return this.nodeRef.hashCode(); + } + + /** + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj instanceof TagScopeImpl) + { + TagScopeImpl that = (TagScopeImpl) obj; + return (this.nodeRef.equals(that.nodeRef)); + } + else + { + return false; + } + } +} diff --git a/source/java/org/alfresco/repo/tagging/TaggingServiceImpl.java b/source/java/org/alfresco/repo/tagging/TaggingServiceImpl.java new file mode 100644 index 0000000000..e2fcc4e534 --- /dev/null +++ b/source/java/org/alfresco/repo/tagging/TaggingServiceImpl.java @@ -0,0 +1,480 @@ +/* + * Copyright (C) 2005-2008 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.tagging; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ActionService; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +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.CategoryService; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.tagging.TagDetails; +import org.alfresco.service.cmr.tagging.TagScope; +import org.alfresco.service.cmr.tagging.TaggingService; +import org.alfresco.util.ISO9075; + +/** + * Tagging service implementation + * + * @author Roy Wetherall + */ +public class TaggingServiceImpl implements TaggingService +{ + /** Node service */ + private NodeService nodeService; + + /** Categorty Service */ + private CategoryService categoryService; + + /** Search Service */ + private SearchService searchService; + + /** Action Service */ + private ActionService actionService; + + /** Content Service */ + private ContentService contentService; + + /** Tag Details Delimiter */ + private static final String TAG_DETAILS_DELIMITER = "|"; + + /** + * Set the cateogry service + * + * @param categoryService trhe category service + */ + public void setCategoryService(CategoryService categoryService) + { + this.categoryService = categoryService; + } + + /** + * Set the node service + * + * @param nodeService the node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Set the search service + * + * @param searchService the search service + */ + public void setSearchService(SearchService searchService) + { + this.searchService = searchService; + } + + /** + * Set the action service + * + * @return ActionService action service + */ + public void setActionService(ActionService actionService) + { + this.actionService = actionService; + } + + /** + * Set the content service + * + * @param contentService content service + */ + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + + /** + * @see org.alfresco.service.cmr.tagging.TaggingService#isTag(java.lang.String) + */ + public boolean isTag(StoreRef storeRef, String tag) + { + return (getTagNodeRef(storeRef, tag) != null); + } + + /** + * @see org.alfresco.service.cmr.tagging.TaggingService#createTag(java.lang.String) + */ + public void createTag(StoreRef storeRef, String tag) + { + if (isTag(storeRef, tag) == false) + { + this.categoryService.createRootCategory(storeRef, ContentModel.ASPECT_TAGGABLE, tag); + } + } + + /** + * @see org.alfresco.service.cmr.tagging.TaggingService#getTags() + */ + public List getTags(StoreRef storeRef) + { + Collection rootCategories = this.categoryService.getRootCategories(storeRef, ContentModel.ASPECT_TAGGABLE); + List result = new ArrayList(rootCategories.size()); + for (ChildAssociationRef rootCategory : rootCategories) + { + String name = (String)this.nodeService.getProperty(rootCategory.getChildRef(), ContentModel.PROP_NAME); + result.add(name); + } + return result; + } + + /** + * @see org.alfresco.service.cmr.tagging.TaggingService#addTag(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public void addTag(NodeRef nodeRef, String tag) + { + // Get the tag node reference + NodeRef newTagNodeRef = getTagNodeRef(nodeRef.getStoreRef(), tag); + if (newTagNodeRef == null) + { + // Create the new tag + newTagNodeRef = this.categoryService.createRootCategory(nodeRef.getStoreRef(), ContentModel.ASPECT_TAGGABLE, tag); + } + + List tagNodeRefs = new ArrayList(5); + if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TAGGABLE) == false) + { + // Add the aspect + this.nodeService.addAspect(nodeRef, ContentModel.ASPECT_TAGGABLE, null); + } + else + { + // Get the current tags + List currentTagNodes = (List)this.nodeService.getProperty(nodeRef, ContentModel.PROP_TAGS); + if (currentTagNodes != null) + { + tagNodeRefs = currentTagNodes; + } + } + + // Add the new tag (assuming it's not already been added + if (tagNodeRefs.contains(newTagNodeRef) == false) + { + tagNodeRefs.add(newTagNodeRef); + this.nodeService.setProperty(nodeRef, ContentModel.PROP_TAGS, (Serializable)tagNodeRefs); + updateTagScope(nodeRef, tag, true); + } + } + + /** + * Gets the node reference for a given tag. + *

+ * Returns null if tag is not present. + * + * @param storeRef store reference + * @param tag tag + * @return NodeRef tag node reference or null not exist + */ + private NodeRef getTagNodeRef(StoreRef storeRef, String tag) + { + NodeRef tagNodeRef = null; + String query = "+PATH:\"cm:taggable/cm:" + ISO9075.encode(tag) + "\""; + ResultSet resultSet = this.searchService.query(storeRef, SearchService.LANGUAGE_LUCENE, query); + if (resultSet.length() != 0) + { + tagNodeRef = resultSet.getNodeRef(0); + } + + return tagNodeRef; + } + + /** + * Update the relevant tag scopes when a tag is added or removed from a node. + * + * @param nodeRef node reference + * @param tag tag + * @param add if true then the tag is added, false if the tag is removed + */ + private void updateTagScope(NodeRef nodeRef, String tag, boolean add) + { + Action action = this.actionService.createAction(UpdateTagScopesActionExecuter.NAME); + action.setParameterValue(UpdateTagScopesActionExecuter.PARAM_TAG_NAME, tag); + action.setParameterValue(UpdateTagScopesActionExecuter.PARAM_ADD_TAG, add); + this.actionService.executeAction(action, nodeRef, false, true); + } + + /** + * @see org.alfresco.service.cmr.tagging.TaggingService#removeTag(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public void removeTag(NodeRef nodeRef, String tag) + { + // Check for the taggable aspect + if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TAGGABLE) == true) + { + // Get the tag node reference + NodeRef newTagNodeRef = getTagNodeRef(nodeRef.getStoreRef(), tag); + if (newTagNodeRef != null) + { + // Get the current tags + List currentTagNodes = (List)this.nodeService.getProperty(nodeRef, ContentModel.PROP_TAGS); + if (currentTagNodes != null && + currentTagNodes.size() != 0 && + currentTagNodes.contains(newTagNodeRef) == true) + { + currentTagNodes.remove(newTagNodeRef); + this.nodeService.setProperty(nodeRef, ContentModel.PROP_TAGS, (Serializable)currentTagNodes); + updateTagScope(nodeRef, tag, false); + } + } + } + } + + /** + * @see org.alfresco.service.cmr.tagging.TaggingService#getTags(org.alfresco.service.cmr.repository.NodeRef) + */ + public List getTags(NodeRef nodeRef) + { + List result = new ArrayList(10); + + // Check for the taggable aspect + if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TAGGABLE) == true) + { + // Get the current tags + List currentTagNodes = (List)this.nodeService.getProperty(nodeRef, ContentModel.PROP_TAGS); + if (currentTagNodes != null) + { + for (NodeRef currentTagNode : currentTagNodes) + { + String tag = (String)this.nodeService.getProperty(currentTagNode, ContentModel.PROP_NAME); + result.add(tag); + } + } + } + + return result; + } + + /** + * @see org.alfresco.service.cmr.tagging.TaggingService#addTagScope(org.alfresco.service.cmr.repository.NodeRef) + */ + public void addTagScope(NodeRef nodeRef) + { + if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TAGSCOPE) == false) + { + this.nodeService.addAspect(nodeRef, ContentModel.ASPECT_TAGSCOPE, null); + } + + } + + /** + * @see org.alfresco.service.cmr.tagging.TaggingService#removeTagScope(org.alfresco.service.cmr.repository.NodeRef) + */ + public void removeTagScope(NodeRef nodeRef) + { + if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TAGSCOPE) == true) + { + this.nodeService.removeAspect(nodeRef, ContentModel.ASPECT_TAGSCOPE); + } + } + + /** + * @see org.alfresco.service.cmr.tagging.TaggingService#findTagScope(org.alfresco.service.cmr.repository.NodeRef) + */ + public TagScope findTagScope(NodeRef nodeRef) + { + TagScope tagScope = null; + + if (this.nodeService.exists(nodeRef) == true) + { + List tagScopeNodeRefs = new ArrayList(3); + getTagScopes(nodeRef, tagScopeNodeRefs); + if (tagScopeNodeRefs.size() != 0) + { + tagScope = new TagScopeImpl(tagScopeNodeRefs.get(0), getTagDetails(tagScopeNodeRefs.get(0))); + } + } + + return tagScope; + } + + /** + * Gets the tag details list for a given tag scope node reference + * + * @param nodeRef tag scope node reference + * @return List ordered list of tag details for the tag scope + */ + private List getTagDetails(NodeRef nodeRef) + { + List tagDetails = new ArrayList(13); + ContentReader reader = this.contentService.getReader(nodeRef, ContentModel.PROP_TAGSCOPE_CACHE); + if (reader != null) + { + tagDetails = TaggingServiceImpl.readTagDetails(reader.getContentInputStream()); + } + return tagDetails; + } + + /** + * @see org.alfresco.service.cmr.tagging.TaggingService#findAllTagScopes(org.alfresco.service.cmr.repository.NodeRef) + */ + public List findAllTagScopes(NodeRef nodeRef) + { + List result = null; + + if (this.nodeService.exists(nodeRef) == true) + { + List tagScopeNodeRefs = new ArrayList(3); + getTagScopes(nodeRef, tagScopeNodeRefs); + if (tagScopeNodeRefs.size() != 0) + { + result = new ArrayList(tagScopeNodeRefs.size()); + for (NodeRef tagScopeNodeRef : tagScopeNodeRefs) + { + result.add(new TagScopeImpl(tagScopeNodeRef, getTagDetails(tagScopeNodeRef))); + } + } + else + { + result = Collections.emptyList(); + } + } + + return result; + } + + /** + * Traverses up the node's primary parent placing all tag scope's in a list. + *

+ * If none are found then the list is empty. + * + * @param nodeRef node reference + * @param tagScopes list of tag scopes + */ + private void getTagScopes(NodeRef nodeRef, List tagScopes) + { + if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TAGSCOPE) == true) + { + tagScopes.add(nodeRef); + } + + ChildAssociationRef assoc = this.nodeService.getPrimaryParent(nodeRef); + if (assoc != null) + { + NodeRef parent = assoc.getParentRef(); + if (parent != null) + { + getTagScopes(parent, tagScopes); + } + } + } + + /** + * @see org.alfresco.service.cmr.tagging.TaggingService#findTaggedNodes(java.lang.String) + */ + public List findTaggedNodes(String tag) + { + // TODO + return null; + } + + /** + * @see org.alfresco.service.cmr.tagging.TaggingService#findTaggedNodes(java.lang.String, org.alfresco.service.cmr.tagging.TagScope) + */ + public List findTaggedNodes(String tag, TagScope tagScope) + { + // TODO + return null; + } + + /** + * + * @param is + * @return + */ + /*package*/ static List readTagDetails(InputStream is) + { + List result = new ArrayList(25); + + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + try + { + String nextLine = reader.readLine(); + while (nextLine != null) + { + String[] values = nextLine.split("\\" + TAG_DETAILS_DELIMITER); + result.add(new TagDetailsImpl(values[0], Integer.parseInt(values[1]))); + + nextLine = reader.readLine(); + } + } + catch (IOException exception) + { + + throw new AlfrescoRuntimeException("Unable to read tag details", exception); + } + + return result; + } + + /** + * + * @param tagDetails + * @param os + */ + /*package*/ static String tagDetailsToString(List tagDetails) + { + StringBuffer result = new StringBuffer(255); + + boolean bFirst = true; + for (TagDetails details : tagDetails) + { + if (bFirst == false) + { + result.append("\n"); + } + else + { + bFirst = false; + } + + result.append(details.getTagName()); + result.append(TAG_DETAILS_DELIMITER); + result.append(details.getTagCount()); + } + + return result.toString(); + } +} diff --git a/source/java/org/alfresco/repo/tagging/TaggingServiceImplTest.java b/source/java/org/alfresco/repo/tagging/TaggingServiceImplTest.java new file mode 100644 index 0000000000..6ff2e08f68 --- /dev/null +++ b/source/java/org/alfresco/repo/tagging/TaggingServiceImplTest.java @@ -0,0 +1,484 @@ +/* + * Copyright (C) 2005-2008 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.tagging; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.transaction.UserTransaction; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.service.cmr.action.ActionService; +import org.alfresco.service.cmr.repository.ContentService; +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.security.AuthenticationService; +import org.alfresco.service.cmr.tagging.TagDetails; +import org.alfresco.service.cmr.tagging.TagScope; +import org.alfresco.service.cmr.tagging.TaggingService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.BaseAlfrescoSpringTest; + +/** + * Tagging service implementation unit test + * + * @author Roy Wetherall + */ +public class TaggingServiceImplTest extends BaseAlfrescoSpringTest +{ + /** Services */ + private TaggingService taggingService; + + private static StoreRef storeRef; + private static NodeRef rootNode; + private NodeRef folder; + private NodeRef subFolder; + private NodeRef document; + private NodeRef subDocument; + + private static final String TAG_1 = "tagOne"; + private static final String TAG_2 = "tagTwo"; + private static final String TAG_3 = "tagThree"; + + private static boolean init = false; + + @Override + protected void onSetUpBeforeTransaction() throws Exception + { + super.onSetUpBeforeTransaction(); + + // Get services + this.taggingService = (TaggingService)this.applicationContext.getBean("TaggingService"); + this.nodeService = (NodeService) this.applicationContext.getBean("nodeService"); + this.contentService = (ContentService) this.applicationContext.getBean("contentService"); + this.authenticationService = (AuthenticationService) this.applicationContext.getBean("authenticationService"); + this.actionService = (ActionService)this.applicationContext.getBean("actionService"); + this.transactionService = (TransactionService)this.applicationContext.getBean("transactionComponent"); + + if (init == false) + { + UserTransaction tx = this.transactionService.getUserTransaction(); + tx.begin(); + + // Authenticate as the system user + AuthenticationComponent authenticationComponent = (AuthenticationComponent) this.applicationContext + .getBean("authenticationComponent"); + authenticationComponent.setSystemUserAsCurrentUser(); + + // Create the store and get the root node + TaggingServiceImplTest.storeRef = this.nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis()); + TaggingServiceImplTest.rootNode = this.nodeService.getRootNode(TaggingServiceImplTest.storeRef); + + // Create the required tagging category + NodeRef catContainer = nodeService.createNode(TaggingServiceImplTest.rootNode, ContentModel.ASSOC_CHILDREN, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "categoryContainer"), ContentModel.TYPE_CONTAINER).getChildRef(); + NodeRef catRoot = nodeService.createNode( + catContainer, + ContentModel.ASSOC_CHILDREN, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "categoryRoot"), + ContentModel.TYPE_CATEGORYROOT).getChildRef(); + nodeService.createNode( + catRoot, + ContentModel.ASSOC_CATEGORIES, + ContentModel.ASPECT_TAGGABLE, + ContentModel.TYPE_CATEGORY).getChildRef(); + + init = true; + + tx.commit(); + } + } + + @Override + protected void onSetUpInTransaction() throws Exception + { + // Authenticate as the system user + AuthenticationComponent authenticationComponent = (AuthenticationComponent) this.applicationContext + .getBean("authenticationComponent"); + authenticationComponent.setSystemUserAsCurrentUser(); + + // Create a folder + Map folderProps = new HashMap(1); + folderProps.put(ContentModel.PROP_NAME, "testFolder"); + folder = this.nodeService.createNode( + TaggingServiceImplTest.rootNode, + ContentModel.ASSOC_CHILDREN, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "testFolder"), + ContentModel.TYPE_FOLDER, + folderProps).getChildRef(); + + // Create a node + Map docProps = new HashMap(1); + docProps.put(ContentModel.PROP_NAME, "testDocument.txt"); + document = this.nodeService.createNode( + folder, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "testDocument.txt"), + ContentModel.TYPE_CONTENT, + docProps).getChildRef(); + + Map props = new HashMap(1); + props.put(ContentModel.PROP_NAME, "subFolder"); + subFolder = this.nodeService.createNode( + folder, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "subFolder"), + ContentModel.TYPE_FOLDER, + props).getChildRef(); + + props = new HashMap(1); + props.put(ContentModel.PROP_NAME, "subDocument.txt"); + subDocument = this.nodeService.createNode( + subFolder, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "subDocument.txt"), + ContentModel.TYPE_CONTENT, + props).getChildRef(); + } + + public void testTagCRUD() + throws Exception + { + // Get the tags + List tags = this.taggingService.getTags(TaggingServiceImplTest.storeRef); + assertNotNull(tags); + assertEquals(0, tags.size()); + + // Create a tag + this.taggingService.createTag(TaggingServiceImplTest.storeRef, TAG_1); + + setComplete(); + endTransaction(); + + UserTransaction tx = this.transactionService.getUserTransaction(); + tx.begin(); + + // Get all the tags + tags = this.taggingService.getTags(TaggingServiceImplTest.storeRef); + assertNotNull(tags); + assertEquals(1, tags.size()); + assertTrue(tags.contains(TAG_1)); + + // Check isTag method + assertFalse(this.taggingService.isTag(TaggingServiceImplTest.storeRef, TAG_2)); + assertTrue(this.taggingService.isTag(TaggingServiceImplTest.storeRef, TAG_1)); + + tx.commit(); + } + + public void testAddRemoveTag() + throws Exception + { + List tags = this.taggingService.getTags(this.document); + assertNotNull(tags); + assertTrue(tags.isEmpty()); + + assertTrue(this.taggingService.isTag(TaggingServiceImplTest.storeRef, TAG_1)); + this.taggingService.addTag(this.document, TAG_1); + + tags = this.taggingService.getTags(this.document); + assertNotNull(tags); + assertEquals(1, tags.size()); + assertTrue(tags.contains(TAG_1)); + + assertFalse(this.taggingService.isTag(TaggingServiceImplTest.storeRef, TAG_2)); + this.taggingService.addTag(this.document, TAG_2); + + assertTrue(this.taggingService.isTag(TaggingServiceImplTest.storeRef, TAG_2)); + tags = this.taggingService.getTags(this.document); + assertNotNull(tags); + assertEquals(2, tags.size()); + assertTrue(tags.contains(TAG_1)); + assertTrue(tags.contains(TAG_2)); + + this.taggingService.removeTag(this.document, TAG_1); + tags = this.taggingService.getTags(this.document); + assertNotNull(tags); + assertEquals(1, tags.size()); + assertFalse(tags.contains(TAG_1)); + assertTrue(tags.contains(TAG_2)); + } + + public void testTagScopeFindAddRemove() + { + // Get scopes for node without + TagScope tagScope = this.taggingService.findTagScope(this.subDocument); + assertNull(tagScope); + List tagScopes = this.taggingService.findAllTagScopes(this.subDocument); + assertNotNull(tagScopes); + assertEquals(0, tagScopes.size()); + + // Add scopes + // TODO should the add return the created scope ?? + this.taggingService.addTagScope(this.folder); + this.taggingService.addTagScope(this.subFolder); + + // Get the scopes + tagScope = this.taggingService.findTagScope(this.subDocument); + assertNotNull(tagScope); + tagScopes = this.taggingService.findAllTagScopes(this.subDocument); + assertNotNull(tagScopes); + assertEquals(2, tagScopes.size()); + tagScope = this.taggingService.findTagScope(this.subFolder); + assertNotNull(tagScope); + tagScopes = this.taggingService.findAllTagScopes(this.subFolder); + assertNotNull(tagScopes); + assertEquals(2, tagScopes.size()); + tagScope = this.taggingService.findTagScope(this.folder); + assertNotNull(tagScope); + tagScopes = this.taggingService.findAllTagScopes(this.folder); + assertNotNull(tagScopes); + assertEquals(1, tagScopes.size()); + + // Remove a scope + this.taggingService.removeTagScope(this.folder); + tagScope = this.taggingService.findTagScope(this.subDocument); + assertNotNull(tagScope); + tagScopes = this.taggingService.findAllTagScopes(this.subDocument); + assertNotNull(tagScopes); + assertEquals(1, tagScopes.size()); + tagScope = this.taggingService.findTagScope(this.subFolder); + assertNotNull(tagScope); + tagScopes = this.taggingService.findAllTagScopes(this.subFolder); + assertNotNull(tagScopes); + assertEquals(1, tagScopes.size()); + tagScope = this.taggingService.findTagScope(this.folder); + assertNull(tagScope); + tagScopes = this.taggingService.findAllTagScopes(this.folder); + assertNotNull(tagScopes); + assertEquals(0, tagScopes.size()); + } + + public void testTagScope() + throws Exception + { + // TODO add some tags before the scopes are added + + // Add some tag scopes + this.taggingService.addTagScope(this.folder); + this.taggingService.addTagScope(this.subFolder); + + // Get the tag scope + TagScope ts1 = this.taggingService.findTagScope(this.subDocument); + TagScope ts2 = this.taggingService.findTagScope(this.folder); + + setComplete(); + endTransaction(); + + addTag(this.subDocument, TAG_1, 1, ts1.getNodeRef()); + addTag(this.subDocument, TAG_2, 1, ts1.getNodeRef()); + addTag(this.subDocument, TAG_3, 1, ts1.getNodeRef()); + addTag(this.subFolder, TAG_1, 2, ts1.getNodeRef()); + addTag(this.subFolder, TAG_2, 2, ts1.getNodeRef()); + addTag(this.folder, TAG_2, 3, ts2.getNodeRef()); + + UserTransaction tx = this.transactionService.getUserTransaction(); + tx.begin(); + + // re get the tag scopes + ts1 = this.taggingService.findTagScope(this.subDocument); + ts2 = this.taggingService.findTagScope(this.folder); + + // check the order and count of the tagscopes + assertEquals(2, ts1.getTags().get(0).getTagCount()); + assertEquals(2, ts1.getTags().get(1).getTagCount()); + assertEquals(1, ts1.getTags().get(2).getTagCount()); + assertEquals(3, ts2.getTags().get(0).getTagCount()); + assertEquals(TAG_2, ts2.getTags().get(0).getTagName()); + assertEquals(2, ts2.getTags().get(1).getTagCount()); + assertEquals(TAG_1, ts2.getTags().get(1).getTagName()); + assertEquals(1, ts2.getTags().get(2).getTagCount()); + assertEquals(TAG_3, ts2.getTags().get(2).getTagName()); + + tx.commit(); + + removeTag(this.folder, TAG_2, 2, ts2.getNodeRef()); + removeTag(this.subFolder, TAG_2, 1, ts1.getNodeRef()); + removeTag(this.subFolder, TAG_1, 1, ts1.getNodeRef()); + removeTag(this.subDocument, TAG_1, 0, ts1.getNodeRef()); + + tx = this.transactionService.getUserTransaction(); + tx.begin(); + + // re get the tag scopes + ts1 = this.taggingService.findTagScope(this.subDocument); + ts2 = this.taggingService.findTagScope(this.folder); + + assertEquals(2, ts1.getTags().size()); + assertEquals(2, ts2.getTags().size()); + + tx.commit(); + + } + + private void addTag(NodeRef nodeRef, String tag, int tagCount, NodeRef tagScopeNodeRef) + throws Exception + { + UserTransaction tx = this.transactionService.getUserTransaction(); + tx.begin(); + + // Add some tags + this.taggingService.addTag(nodeRef, tag); + + tx.commit(); + + // Wait a bit cos we want the background threads to kick in and update the tag scope + int count = 0; + boolean bCreated = false; + while (true) + { + UserTransaction tx2 = this.transactionService.getUserTransaction(); + tx2.begin(); + + try + { + // Get the tag scope + List tagScopes = this.taggingService.findAllTagScopes(nodeRef); + TagScope checkTagScope = null; + for (TagScope tagScope : tagScopes) + { + if (tagScope.getNodeRef().equals(tagScopeNodeRef) == true) + { + checkTagScope = tagScope; + break; + } + } + assertNotNull(checkTagScope); + + // Check that tag scopes are in the correct order + List tagDetailsList = checkTagScope.getTags(); + for (TagDetails tagDetails : tagDetailsList) + { + if (tagDetails.getTagName().equals(tag) == true && + tagDetails.getTagCount() == tagCount) + { + bCreated = true; + break; + } + } + + if (bCreated == true) + { + break; + } + + // Wait to give the threads a chance to execute + Thread.sleep(1000); + + if (count == 10) + { + fail("The background task to update the tag scope failed"); + } + count ++; + } + finally + { + tx2.commit(); + } + } + } + + private void removeTag(NodeRef nodeRef, String tag, int tagCount, NodeRef tagScopeNodeRef) + throws Exception + { + UserTransaction tx = this.transactionService.getUserTransaction(); + tx.begin(); + + // Add some tags + this.taggingService.removeTag(nodeRef, tag); + + tx.commit(); + + // Wait a bit cos we want the background threads to kick in and update the tag scope + int count = 0; + boolean bRemoved = false; + boolean bMissing = (tagCount == 0); + while (true) + { + UserTransaction tx2 = this.transactionService.getUserTransaction(); + tx2.begin(); + + try + { + // Get the tag scope + List tagScopes = this.taggingService.findAllTagScopes(nodeRef); + TagScope checkTagScope = null; + for (TagScope tagScope : tagScopes) + { + if (tagScope.getNodeRef().equals(tagScopeNodeRef) == true) + { + checkTagScope = tagScope; + break; + } + } + assertNotNull(checkTagScope); + + // Check that tag scopes are in the correct order + boolean bFound = false; + List tagDetailsList = checkTagScope.getTags(); + for (TagDetails tagDetails : tagDetailsList) + { + if (tagDetails.getTagName().equals(tag) == true ) + { + if (tagDetails.getTagCount() == tagCount) + { + bRemoved = true; + } + + bFound = true; + break; + } + } + + if (bRemoved == true) + { + break; + } + else if (bMissing == true && bFound == false) + { + break; + } + + // Wait to give the threads a chance to execute + Thread.sleep(1000); + + if (count == 10) + { + fail("The background task to update the tag scope failed"); + } + count ++; + } + finally + { + tx2.commit(); + } + } + } +} diff --git a/source/java/org/alfresco/repo/tagging/UpdateTagScopesActionExecuter.java b/source/java/org/alfresco/repo/tagging/UpdateTagScopesActionExecuter.java new file mode 100644 index 0000000000..70df26d243 --- /dev/null +++ b/source/java/org/alfresco/repo/tagging/UpdateTagScopesActionExecuter.java @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.tagging; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.action.ParameterDefinitionImpl; +import org.alfresco.repo.action.executer.ActionExecuterAbstractBase; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +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.tagging.TagDetails; +import org.alfresco.service.cmr.tagging.TagScope; +import org.alfresco.service.cmr.tagging.TaggingService; + +/** + * Update tag scopes action executer. + * + * NOTE: This action is used to facilitate the async update of tag scopes. It is not intended for genereral useage. + * + * @author Roy Wetherall + */ +public class UpdateTagScopesActionExecuter extends ActionExecuterAbstractBase +{ + /** Node Service */ + private NodeService nodeService; + + /** Content Service */ + private ContentService contentService; + + /** Tagging Service */ + private TaggingService taggingService; + + /** Action name and parameters */ + public static final String NAME = "update-tagscope"; + public static final String PARAM_TAG_NAME = "tag_name"; + public static final String PARAM_ADD_TAG = "add_tag"; + + /** + * Set the node service + * + * @param nodeService node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Set the content service + * + * @param contentService the content service + */ + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + + /** + * Set the tagging service + * + * @param taggingService the tagging service + */ + public void setTaggingService(TaggingService taggingService) + { + this.taggingService = taggingService; + } + + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + if (this.nodeService.exists(actionedUponNodeRef) == true) + { + // Get the parameter values + String tagName = (String)action.getParameterValue(PARAM_TAG_NAME); + Boolean isAdd = (Boolean)action.getParameterValue(PARAM_ADD_TAG); + + // Get the tag scopes for the actioned upon node + List tagScopes = this.taggingService.findAllTagScopes(actionedUponNodeRef); + + // Update each tag scope + for (TagScope tagScope : tagScopes) + { + NodeRef tagScopeNodeRef = tagScope.getNodeRef(); + List tags = null; + + // Get the current tags + ContentReader contentReader = this.contentService.getReader(tagScopeNodeRef, ContentModel.PROP_TAGSCOPE_CACHE); + if (contentReader == null) + { + tags = new ArrayList(1); + } + else + { + tags = TaggingServiceImpl.readTagDetails(contentReader.getContentInputStream()); + } + + TagDetails currentTag = null; + for (TagDetails tag : tags) + { + if (tag.getTagName().equals(tagName) == true) + { + currentTag = tag; + break; + } + } + + if (isAdd == true) + { + if (currentTag == null) + { + tags.add(new TagDetailsImpl(tagName, 1)); + } + else + { + ((TagDetailsImpl)currentTag).incrementCount(); + } + + } + else + { + if (currentTag != null) + { + int currentTagCount = currentTag.getTagCount(); + if (currentTagCount == 1) + { + tags.remove(currentTag); + } + else + { + ((TagDetailsImpl)currentTag).decrementCount(); + } + } + } + + // Order the list + Collections.sort(tags); + + // Write new content back to tag scope + String tagContent = TaggingServiceImpl.tagDetailsToString(tags); + ContentWriter contentWriter = this.contentService.getWriter(tagScopeNodeRef, ContentModel.PROP_TAGSCOPE_CACHE, true); + contentWriter.setEncoding("UTF-8"); + contentWriter.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + contentWriter.putContent(tagContent); + + } + } + } + + /** + * @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List) + */ + @Override + protected void addParameterDefinitions(List paramList) + { + paramList.add(new ParameterDefinitionImpl(PARAM_TAG_NAME, DataTypeDefinition.TEXT, true, getParamDisplayLabel(PARAM_TAG_NAME))); + paramList.add(new ParameterDefinitionImpl(PARAM_ADD_TAG, DataTypeDefinition.BOOLEAN, false, getParamDisplayLabel(PARAM_ADD_TAG))); + } + +} diff --git a/source/java/org/alfresco/repo/thumbnail/ThumbnailDetails.java b/source/java/org/alfresco/repo/thumbnail/ThumbnailDetails.java index ff224d50d0..34b317798f 100644 --- a/source/java/org/alfresco/repo/thumbnail/ThumbnailDetails.java +++ b/source/java/org/alfresco/repo/thumbnail/ThumbnailDetails.java @@ -24,8 +24,6 @@ */ package org.alfresco.repo.thumbnail; -import java.lang.reflect.Constructor; - import org.alfresco.service.cmr.repository.TransformationOptions; /** diff --git a/source/java/org/alfresco/service/cmr/preference/PreferenceService.java b/source/java/org/alfresco/service/cmr/preference/PreferenceService.java index 27884e41a9..85bc56f645 100644 --- a/source/java/org/alfresco/service/cmr/preference/PreferenceService.java +++ b/source/java/org/alfresco/service/cmr/preference/PreferenceService.java @@ -27,19 +27,68 @@ package org.alfresco.service.cmr.preference; import java.io.Serializable; import java.util.Map; +import org.alfresco.service.Auditable; + /** * @author Roy Wetherall */ public interface PreferenceService { + /** + * Get all preferences for a particular user + * + * @param userName the user name + * @return Map a map containing the preference values, empty if none + */ + @Auditable(key = Auditable.Key.ARG_0, parameters = {"userName"}) Map getPreferences(String userName); - + + /** + * Get the preferences for a particular user. + *

+ * If no filter if provided all preferences are returned. + *

+ * If a filter is provided it's used to filter the results. For example the filter + * "alfresco.myComp" will only return filters that are in the "namespace" alfresco.myComp. + * + * @param userName the user name + * @param preferenceFilter the preference filter + * @return Map a map containing the preference values, empty if none + */ + @Auditable(key = Auditable.Key.ARG_0, parameters = {"userName", "preferenceFilter"}) Map getPreferences(String userName, String preferenceFilter); + /** + * Sets the preference values for a user. + *

+ * Values provided overlay those already present. + *

+ * Preference value names can be "namespaced" by using package notation. For example + * "alfresc.myComp.myValue". + * + * @param userName the user name + * @param preferences the preference values + */ + @Auditable(key = Auditable.Key.ARG_0, parameters = {"userName", "preferences"}) void setPreferences(String userName, Map preferences); + /** + * Clears all the preferences for a particular user. + * + * @param userName the user name + */ + @Auditable(key = Auditable.Key.ARG_0, parameters = {"userName"}) void clearPreferences(String userName); - + + /** + * Clears the preferences for a particular user that match the filter optionally provided. + *

+ * If no filter if present then all preferences are cleared. + * + * @param userName the user name + * @param preferenceFilter the preference filter + */ + @Auditable(key = Auditable.Key.ARG_0, parameters = {"userName", "preferenceFilter"}) void clearPreferences(String userName, String preferenceFilter); } diff --git a/source/java/org/alfresco/service/cmr/tagging/TagDetails.java b/source/java/org/alfresco/service/cmr/tagging/TagDetails.java new file mode 100644 index 0000000000..0bb8e1c6e9 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/tagging/TagDetails.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2005-2008 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.service.cmr.tagging; + +/** + * Tag details interface. + * + * @author Roy Wetherall + */ +public interface TagDetails extends Comparable +{ + /** + * Get the name of the tag + * + * @return String tag name + */ + String getTagName(); + + /** + * Get the tag count + * + * @return int tag count + */ + int getTagCount(); +} diff --git a/source/java/org/alfresco/service/cmr/tagging/TagScope.java b/source/java/org/alfresco/service/cmr/tagging/TagScope.java new file mode 100644 index 0000000000..b7b29c5dce --- /dev/null +++ b/source/java/org/alfresco/service/cmr/tagging/TagScope.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2005-2008 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.service.cmr.tagging; + +import java.util.List; + +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Tag Scope Inteface. + * + * Represents the roll up of tags within the scope of a node tree. + * + * @author Roy Wetherall + */ +public interface TagScope +{ + NodeRef getNodeRef(); + + List getTags(); + + List getTags(int topN); + + TagDetails getTag(String tag); + + boolean isTagInScope(String tag); +} diff --git a/source/java/org/alfresco/service/cmr/tagging/TaggingService.java b/source/java/org/alfresco/service/cmr/tagging/TaggingService.java new file mode 100644 index 0000000000..a2bd3b3046 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/tagging/TaggingService.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.service.cmr.tagging; + +import java.util.List; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; + +/** + * @author Roy Wetherall + */ +public interface TaggingService +{ + /** + * Indicates whether the tag already exists + * + * @param tag + * @return + */ + boolean isTag(StoreRef storeRef, String tag); + + /** + * Get all the tags currently available + * + * @return + */ + List getTags(StoreRef storeRef); + + /** + * Create a new tag + * + * @param tag + */ + void createTag(StoreRef storeRef, String tag); + + /** + * Add a tag to a node. Creating the tag if it does not already exist. + * + * @param nodeRef + * @param tag + */ + void addTag(NodeRef nodeRef, String tag); + + /** + * Remove a tag from a node. + * + * @param nodeRef + * @param tag + */ + void removeTag(NodeRef nodeRef, String tag); + + /** + * Get all the tags on a node + * + * @param nodeRef + * @return + */ + List getTags(NodeRef nodeRef); + + /** + * Adds a tag scope to the specified node + * + * @param nodeRef node reference + */ + void addTagScope(NodeRef nodeRef); + + /** + * + * @param nodeRef + */ + void removeTagScope(NodeRef nodeRef); + + /** + * Finds the 'nearest' tag scope for the specified node. + *

+ * The 'nearest' tag scope is discovered by walking up the primary parent path + * untill a tag scope is found or the root node is reached. + *

+ * If no tag scope if found then a null value is returned. + * + * @param nodeRef node reference + * @return the 'nearest' tag scope or null if none found + */ + TagScope findTagScope(NodeRef nodeRef); + + /** + * + * @param nodeRef + * @return + */ + List findAllTagScopes(NodeRef nodeRef); + + /** + * Find all nodes that have been tagged with the specified tag. + * + * @param tag tag name + * @return List list of nodes tagged with specified tag, empty of none found + */ + List findTaggedNodes(String tag); + + /** + * Find all nodes that have been tagged with the specified tag and reside within + * the tag scope. + * + * @param tag + * @param tagScope + * @return + */ + List findTaggedNodes(String tag, TagScope tagScope); +} + +