From 6da9f1acc70a77b932d7b54a79394fc41750498d Mon Sep 17 00:00:00 2001 From: Alan Davis Date: Fri, 11 Mar 2016 22:34:24 +0000 Subject: [PATCH] Merged 5.1.N (5.1.1) to HEAD (5.2) 123426 amorarasu: Merged 5.0.N (5.0.4) to 5.1.N (5.1.1) 123344 cturlica: Merged V4.2-BUG-FIX (4.2.7) to 5.0.N (5.0.4) 123045 rmunteanu: Merged V4.2.6 (4.2.6) to V4.2-BUG-FIX (4.2.7) 122858 rneamtu: MNT-15679: Delete comment updates the "Modifier" and "Modified date" properties of a file - Java-backed webscript CommentDelete added - Auditable behavior is disabled around deleting comment-node - Added unit test for case: CommentsApiTest.testDeleteCommentDoesNotChangeModifiedDate git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@123701 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../repository/comments/comment.delete.js | 49 --- .../web-scripts-application-context.xml | 20 +- .../comments/AbstractCommentsWebScript.java | 327 ++++++++++++++++++ .../web/scripts/comments/CommentDelete.java | 120 +++++++ .../web/scripts/comments/CommentsPost.java | 210 +++-------- .../web/scripts/comment/CommentsApiTest.java | 200 ++++++++++- 6 files changed, 708 insertions(+), 218 deletions(-) delete mode 100644 config/alfresco/templates/webscripts/org/alfresco/repository/comments/comment.delete.js create mode 100644 source/java/org/alfresco/repo/web/scripts/comments/AbstractCommentsWebScript.java create mode 100644 source/java/org/alfresco/repo/web/scripts/comments/CommentDelete.java diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/comments/comment.delete.js b/config/alfresco/templates/webscripts/org/alfresco/repository/comments/comment.delete.js deleted file mode 100644 index 4e40727205..0000000000 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/comments/comment.delete.js +++ /dev/null @@ -1,49 +0,0 @@ - - - -/** - * Delete a comment. - */ -function deleteComment(node) -{ - // we simply delete the topic - var nodeRef = node.nodeRef; - var isDeleted = node.remove(); - if (!isDeleted) - { - status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, "Unable to delete node: " + nodeRef); - return; - } - - model.message = "Node " + nodeRef + " deleted"; -} - -function main() -{ - // get requested node - var node = getRequestNode(); - if (status.getCode() != status.STATUS_OK) - { - return; - } - - deleteComment(node); - - // post an activity item, but only if we got a site - if ((args["site"] != undefined) && (args["itemTitle"] != undefined && (args["page"] != undefined))) - { - var params = jsonUtils.toObject(decodeURIComponent(args["pageParams"])), strParams = ""; - for (param in params) - { - strParams += param + "=" + encodeURIComponent(params[param]) + "&"; - } - var data = - { - title: args["itemTitle"], - page: args["page"] + (strParams != "" ? "?" + strParams.substring(0, strParams.length - 1) : "") - } - activities.postActivity("org.alfresco.comments.comment-deleted", args["site"], "comments", jsonUtils.toJSONString(data)); - } -} - -main(); diff --git a/config/alfresco/web-scripts-application-context.xml b/config/alfresco/web-scripts-application-context.xml index fdcdd0b458..0c6f276605 100644 --- a/config/alfresco/web-scripts-application-context.xml +++ b/config/alfresco/web-scripts-application-context.xml @@ -1606,18 +1606,28 @@ - + - + + + - + + + + + + + diff --git a/source/java/org/alfresco/repo/web/scripts/comments/AbstractCommentsWebScript.java b/source/java/org/alfresco/repo/web/scripts/comments/AbstractCommentsWebScript.java new file mode 100644 index 0000000000..55d39aa5ae --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/comments/AbstractCommentsWebScript.java @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2005-2016 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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 . + */ +package org.alfresco.repo.web.scripts.comments; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Map; + +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.policy.BehaviourFilter; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.activities.ActivityService; +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.PermissionService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONStringer; +import org.json.JSONWriter; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * This class is the abstract controller for the comments web scripts (delete + * and post) + * + * @author Ramona Popa + * @since 4.2.6 + */ +public abstract class AbstractCommentsWebScript extends DeclarativeWebScript +{ + + protected final static String COMMENTS_TOPIC_NAME = "Comments"; + + private static Log logger = LogFactory.getLog(CommentsPost.class); + + protected final static String JSON_KEY_SITE = "site"; + protected final static String JSON_KEY_SITE_ID = "siteid"; + protected final static String JSON_KEY_ITEM_TITLE = "itemTitle"; + protected final static String JSON_KEY_PAGE = "page"; + protected final static String JSON_KEY_TITLE = "title"; + protected final static String JSON_KEY_PAGE_PARAMS = "pageParams"; + protected final static String JSON_KEY_NODEREF = "nodeRef"; + protected final static String JSON_KEY_CONTENT = "content"; + + protected final static String COMMENT_CREATED_ACTIVITY = "org.alfresco.comments.comment-created"; + protected final static String COMMENT_DELETED_ACTIVITY = "org.alfresco.comments.comment-deleted"; + + protected ServiceRegistry serviceRegistry; + protected NodeService nodeService; + protected ContentService contentService; + protected PersonService personService; + protected SiteService siteService; + protected PermissionService permissionService; + protected ActivityService activityService; + + protected BehaviourFilter behaviourFilter; + + protected static final String PARAM_MESSAGE = "message"; + protected static final String PARAM_NODE = "node"; + protected static final String PARAM_ITEM = "item"; + + public void setServiceRegistry(ServiceRegistry serviceRegistry) + { + this.serviceRegistry = serviceRegistry; + this.nodeService = serviceRegistry.getNodeService(); + this.siteService = serviceRegistry.getSiteService(); + this.contentService = serviceRegistry.getContentService(); + this.personService = serviceRegistry.getPersonService(); + this.permissionService = serviceRegistry.getPermissionService(); + } + + public void setBehaviourFilter(BehaviourFilter behaviourFilter) + { + this.behaviourFilter = behaviourFilter; + } + + public void setActivityService(ActivityService activityService) + { + this.activityService = activityService; + } + + /** + * returns the nodeRef from web script request + * @param req + * @return + */ + protected NodeRef parseRequestForNodeRef(WebScriptRequest req) + { + Map templateVars = req.getServiceMatch().getTemplateVars(); + String storeType = templateVars.get("store_type"); + String storeId = templateVars.get("store_id"); + String nodeId = templateVars.get("id"); + + // create the NodeRef and ensure it is valid + StoreRef storeRef = new StoreRef(storeType, storeId); + return new NodeRef(storeRef, nodeId); + } + + /** + * get the value from JSON for given key if exists + * @param json + * @param key + * @return + */ + protected String getOrNull(JSONObject json, String key) + { + if (json != null && json.containsKey(key)) + { + return (String) json.get(key); + } + return null; + } + + /** + * parse JSON from request + * @param req + * @return + */ + protected JSONObject parseJSON(WebScriptRequest req) + { + JSONObject json = null; + String contentType = req.getContentType(); + if (contentType != null && contentType.indexOf(';') != -1) + { + contentType = contentType.substring(0, contentType.indexOf(';')); + } + if (MimetypeMap.MIMETYPE_JSON.equals(contentType)) + { + JSONParser parser = new JSONParser(); + try + { + json = (JSONObject) parser.parse(req.getContent().getContent()); + } + catch (IOException io) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid JSON: " + io.getMessage()); + } + catch (ParseException pe) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid JSON: " + pe.getMessage()); + } + } + return json; + } + + /** + * parse JSON for a given input string + * @param input + * @return + */ + protected JSONObject parseJSONFromString(String input) + { + JSONObject json = null; + + JSONParser parser = new JSONParser(); + try + { + json = (JSONObject) parser.parse(input); + } + catch (ParseException pe) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid JSON: " + pe.getMessage()); + } + + return json; + } + + /** + * Post an activity entry for the comment added or deleted + * + * @param json + * - is not sent null with this activity type - only for delete + * @param req + * @param nodeRef + * @param activityType + */ + protected void postActivity(JSONObject json, WebScriptRequest req, NodeRef nodeRef, String activityType) + { + String jsonActivityData = ""; + String siteId = ""; + String page = ""; + String title = ""; + String strNodeRef = nodeRef.toString(); + SiteInfo siteInfo = getSiteInfo(req, COMMENT_CREATED_ACTIVITY.equals(activityType)); + + + // post an activity item, but only if we've got a site + if (siteInfo != null) + { + siteId = siteInfo.getShortName(); + if (siteId == null || siteId.length() == 0) + { + return; + } + } + + // json is not sent null with this activity type - only for delete + if (COMMENT_CREATED_ACTIVITY.equals(activityType)) + { + try + { + org.json.JSONObject params = new org.json.JSONObject(getOrNull(json, JSON_KEY_PAGE_PARAMS)); + String strParams = ""; + + Iterator itr = params.keys(); + while (itr.hasNext()) + { + String strParam = itr.next().toString(); + strParams += strParam + "=" + params.getString(strParam) + "&"; + } + page = getOrNull(json, JSON_KEY_PAGE) + "?" + (strParams != "" ? strParams.substring(0, strParams.length() - 1) : ""); + title = getOrNull(json, JSON_KEY_ITEM_TITLE); + + } + catch (Exception e) + { + logger.warn("Error parsing JSON", e); + } + } + else + { + // COMMENT_DELETED_ACTIVITY + title = req.getParameter(JSON_KEY_ITEM_TITLE); + page = req.getParameter(JSON_KEY_PAGE) + "?" + JSON_KEY_NODEREF + "=" + strNodeRef; + } + + try + { + JSONWriter jsonWriter = new JSONStringer().object(); + jsonWriter.key(JSON_KEY_TITLE).value(title); + jsonWriter.key(JSON_KEY_PAGE).value(page); + jsonWriter.key(JSON_KEY_NODEREF).value(strNodeRef); + + jsonActivityData = jsonWriter.endObject().toString(); + activityService.postActivity(activityType, siteId, COMMENTS_TOPIC_NAME, jsonActivityData); + } + catch (Exception e) + { + logger.warn("Error adding comment to activities feed", e); + } + + } + + /** + * returns SiteInfo needed for post activity + * @param req + * @return + */ + protected SiteInfo getSiteInfo(WebScriptRequest req, boolean searchForSiteInJSON) + { + String siteName = req.getParameter(JSON_KEY_SITE); + + if (siteName == null && searchForSiteInJSON ) + { + JSONObject json = parseJSON(req); + if (json != null){ + if (json.containsKey(JSON_KEY_SITE)) + { + siteName = (String) json.get(JSON_KEY_SITE); + } + else if (json.containsKey(JSON_KEY_SITE_ID)) + { + siteName = (String) json.get(JSON_KEY_SITE_ID); + } + } + } + if (siteName != null) + { + SiteInfo site = siteService.getSite(siteName); + return site; + } + + return null; + } + + /** + * Overrides DeclarativeWebScript with parse request for nodeRef + */ + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + // get requested node + NodeRef nodeRef = parseRequestForNodeRef(req); + + // Have the real work done + return executeImpl(nodeRef, req, status, cache); + } + + /** + * + * @param nodeRef + * @param req + * @param status + * @param cache + * @return + */ + protected abstract Map executeImpl(NodeRef nodeRef, WebScriptRequest req, Status status, Cache cache); + +} diff --git a/source/java/org/alfresco/repo/web/scripts/comments/CommentDelete.java b/source/java/org/alfresco/repo/web/scripts/comments/CommentDelete.java new file mode 100644 index 0000000000..516be6ce41 --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/comments/CommentDelete.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2005-2016 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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 . + */ +package org.alfresco.repo.web.scripts.comments; + +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.model.ForumModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.simple.JSONObject; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * This class is the controller for the comment.delete web script. + * + * @author Ramona Popa + * @since 4.2.6 + */ + +public class CommentDelete extends AbstractCommentsWebScript +{ + + private static Log logger = LogFactory.getLog(CommentDelete.class); + + /** + * Overrides AbstractCommentsWebScript to delete comment + */ + @Override + protected Map executeImpl(NodeRef nodeRef, WebScriptRequest req, Status status, Cache cache) + { + String pageParams = req.getParameter(JSON_KEY_PAGE_PARAMS); + + JSONObject jsonPageParams = parseJSONFromString(pageParams); + + NodeRef parentNodeRef = new NodeRef((String) getOrNull(jsonPageParams, JSON_KEY_NODEREF)); + + this.behaviourFilter.disableBehaviour(parentNodeRef, ContentModel.ASPECT_AUDITABLE); + + try + { + // delete node + deleteComment(nodeRef); + + if (nodeService.exists(nodeRef)) + { + // comment was not removed + status.setCode(Status.STATUS_INTERNAL_SERVER_ERROR, "Unable to delete node: " + nodeRef); + return null; + } + + // generate response model for a comment node + Map model = generateModel(nodeRef); + + // post an activity item - it is ok to send json as null since the + // infos that we need are as parameters on request + postActivity(null, req, parentNodeRef, COMMENT_DELETED_ACTIVITY); + + status.setCode(Status.STATUS_OK); + return model; + + } + finally + { + this.behaviourFilter.enableBehaviour(parentNodeRef, ContentModel.ASPECT_AUDITABLE); + } + } + + /** + * deletes comment node + * + * @param commentNodeRef + */ + private void deleteComment(NodeRef commentNodeRef) + { + QName nodeType = nodeService.getType(commentNodeRef); + if (!nodeType.equals(ForumModel.TYPE_POST)) + { + throw new IllegalArgumentException("Node to delete is not a comment node."); + } + + nodeService.deleteNode(commentNodeRef); + } + + /** + * generates model for delete comment script + * + * @param commentNodeRef + * @return + */ + private Map generateModel(NodeRef commentNodeRef) + { + Map model = new HashMap(2, 1.0f); + + model.put(PARAM_MESSAGE, "Node " + commentNodeRef + " deleted"); + + return model; + } +} diff --git a/source/java/org/alfresco/repo/web/scripts/comments/CommentsPost.java b/source/java/org/alfresco/repo/web/scripts/comments/CommentsPost.java index 5194d5a836..61e761a2b5 100644 --- a/source/java/org/alfresco/repo/web/scripts/comments/CommentsPost.java +++ b/source/java/org/alfresco/repo/web/scripts/comments/CommentsPost.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2014 Alfresco Software Limited. + * Copyright (C) 2005-2016 Alfresco Software Limited. * * This file is part of Alfresco * @@ -18,47 +18,28 @@ */ package org.alfresco.repo.web.scripts.comments; -import java.io.IOException; import java.io.Serializable; import java.util.Date; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import org.alfresco.model.ContentModel; import org.alfresco.model.ForumModel; -import org.alfresco.repo.Client; -import org.alfresco.repo.Client.ClientType; import org.alfresco.repo.content.MimetypeMap; -import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.site.SiteModel; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.activities.ActivityService; import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.security.AccessStatus; import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.json.JSONStringer; -import org.json.JSONWriter; import org.json.simple.JSONObject; -import org.json.simple.parser.JSONParser; -import org.json.simple.parser.ParseException; import org.springframework.extensions.webscripts.Cache; -import org.springframework.extensions.webscripts.DeclarativeWebScript; import org.springframework.extensions.webscripts.Status; -import org.springframework.extensions.webscripts.WebScriptException; import org.springframework.extensions.webscripts.WebScriptRequest; /** @@ -68,33 +49,14 @@ import org.springframework.extensions.webscripts.WebScriptRequest; * @since 4.1.7.1 */ -public class CommentsPost extends DeclarativeWebScript { - private final static String COMMENTS_TOPIC_NAME = "Comments"; - - private static Log logger = LogFactory.getLog(CommentsPost.class); - - private final static String JSON_KEY_SITE = "site"; - private final static String JSON_KEY_ITEM_TITLE = "itemTitle"; - private final static String JSON_KEY_PAGE = "page"; - private final static String JSON_KEY_TITLE = "title"; - private final static String JSON_KEY_PAGE_PARAMS = "pageParams"; - private final static String JSON_KEY_NODEREF = "nodeRef"; - private final static String JSON_KEY_CONTENT = "content"; - - private ServiceRegistry serviceRegistry; - private NodeService nodeService; - private ContentService contentService; - private PersonService personService; - private PermissionService permissionService; - private ActivityService activityService; - - private BehaviourFilter behaviourFilter; - +public class CommentsPost extends AbstractCommentsWebScript +{ + /** + * Overrides AbstractCommentsWebScript to add comment + */ @Override - protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + protected Map executeImpl(NodeRef nodeRef, WebScriptRequest req, Status status, Cache cache) { - // get requested node - NodeRef nodeRef = parseRequestForNodeRef(req); // get json object from request JSONObject json = parseJSON(req); @@ -110,7 +72,7 @@ public class CommentsPost extends DeclarativeWebScript { Map model = generateModel(nodeRef, commentNodeRef); // post an activity item - postActivity(json, nodeRef); + postActivity(json, req, nodeRef, COMMENT_CREATED_ACTIVITY); return model; } @@ -119,49 +81,14 @@ public class CommentsPost extends DeclarativeWebScript { this.behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE); } } - - private void postActivity(JSONObject json, NodeRef nodeRef) - { - // post an activity item, but only if we've got a site - if(json.containsKey(JSON_KEY_SITE) && json.containsKey(JSON_KEY_ITEM_TITLE) && json.containsKey(JSON_KEY_PAGE)) - { - String siteId = getOrNull(json, JSON_KEY_SITE); - if (siteId != null && siteId != "") - { - try - { - org.json.JSONObject params = new org.json.JSONObject(getOrNull(json, JSON_KEY_PAGE_PARAMS)); - String strParams = ""; - - Iterator itr = params.keys(); - while (itr.hasNext()) - { - String strParam = itr.next().toString(); - strParams += strParam + "=" + params.getString(strParam) + "&"; - } - String page = getOrNull(json, JSON_KEY_PAGE) + "?" + (strParams != "" ? strParams.substring(0, strParams.length()-1) : ""); - String title = getOrNull(json, JSON_KEY_ITEM_TITLE); - String strNodeRef = nodeRef.toString(); - - JSONWriter jsonWriter = new JSONStringer().object(); - - jsonWriter.key(JSON_KEY_TITLE).value(title); - jsonWriter.key(JSON_KEY_PAGE).value(page); - jsonWriter.key(JSON_KEY_NODEREF).value(strNodeRef); - - String jsonActivityData = jsonWriter.endObject().toString(); - - activityService.postActivity("org.alfresco.comments.comment-created", siteId, "comments", jsonActivityData, Client.asType(ClientType.webclient)); - } - catch(Exception e) - { - logger.warn("Error adding comment to activities feed", e); - } - } - } - - } - + + /** + * add the comment from json to given nodeRef + * + * @param nodeRef + * @param json + * @return + */ private NodeRef addComment(NodeRef nodeRef, JSONObject json) { // fetch the parent to add the node to @@ -192,6 +119,12 @@ public class CommentsPost extends DeclarativeWebScript { return commentNodeRef; } + /** + * generates an comment item value + * + * @param commentNodeRef + * @return + */ private Map generateItemValue(NodeRef commentNodeRef) { Map result = new HashMap(4, 1.0f); @@ -222,52 +155,28 @@ public class CommentsPost extends DeclarativeWebScript { return result; } + /** + * generates the response model for adding a comment + * + * @param nodeRef + * @param commentNodeRef + * @return + */ private Map generateModel(NodeRef nodeRef, NodeRef commentNodeRef) { Map model = new HashMap(2, 1.0f); - - model.put("node", nodeRef); - model.put("item", generateItemValue(commentNodeRef)); - + + model.put(PARAM_NODE, nodeRef); + model.put(PARAM_ITEM, generateItemValue(commentNodeRef)); + return model; } - - private String getOrNull(JSONObject json, String key) - { - if (json.containsKey(key)) - { - return (String)json.get(key); - } - return null; - } - - private JSONObject parseJSON(WebScriptRequest req) - { - JSONObject json = null; - String contentType = req.getContentType(); - if (contentType != null && contentType.indexOf(';') != -1) - { - contentType = contentType.substring(0, contentType.indexOf(';')); - } - if (MimetypeMap.MIMETYPE_JSON.equals(contentType)) - { - JSONParser parser = new JSONParser(); - try - { - json = (JSONObject)parser.parse(req.getContent().getContent()); - } - catch (IOException io) - { - throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid JSON: " + io.getMessage()); - } - catch(ParseException pe) - { - throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid JSON: " + pe.getMessage()); - } - } - return json; - } - + + /** + * + * @param nodeRef + * @return + */ private NodeRef getOrCreateCommentsFolder(NodeRef nodeRef) { NodeRef commentsFolder = getCommentsFolder(nodeRef); @@ -279,6 +188,12 @@ public class CommentsPost extends DeclarativeWebScript { return commentsFolder; } + /** + * returns the nodeRef of the existing one + * + * @param nodeRef + * @return + */ private NodeRef getCommentsFolder(NodeRef nodeRef) { if (nodeService.hasAspect(nodeRef, ForumModel.ASPECT_DISCUSSABLE)) @@ -293,24 +208,18 @@ public class CommentsPost extends DeclarativeWebScript { return null; } } - - private NodeRef parseRequestForNodeRef(WebScriptRequest req) - { - Map templateVars = req.getServiceMatch().getTemplateVars(); - String storeType = templateVars.get("store_type"); - String storeId = templateVars.get("store_id"); - String nodeId = templateVars.get("id"); - - // create the NodeRef and ensure it is valid - StoreRef storeRef = new StoreRef(storeType, storeId); - return new NodeRef(storeRef, nodeId); - } - + private String getUniqueChildName(String prefix) { return prefix + "-" + System.currentTimeMillis(); } + /** + * creates the comments folder if it does not exists + * + * @param nodeRef + * @return + */ private NodeRef createCommentsFolder(final NodeRef nodeRef) { NodeRef commentsFolder = AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() @@ -360,19 +269,4 @@ public class CommentsPost extends DeclarativeWebScript { return commentsFolder; } - public void setServiceRegistry(ServiceRegistry serviceRegistry) { - this.serviceRegistry = serviceRegistry; - this.nodeService = serviceRegistry.getNodeService(); - this.contentService = serviceRegistry.getContentService(); - this.personService = serviceRegistry.getPersonService(); - this.permissionService = serviceRegistry.getPermissionService(); - } - - public void setBehaviourFilter(BehaviourFilter behaviourFilter) { - this.behaviourFilter = behaviourFilter; - } - - public void setActivityService(ActivityService activityService) { - this.activityService = activityService; - } } diff --git a/source/test-java/org/alfresco/repo/web/scripts/comment/CommentsApiTest.java b/source/test-java/org/alfresco/repo/web/scripts/comment/CommentsApiTest.java index 910918a8a8..6660442811 100644 --- a/source/test-java/org/alfresco/repo/web/scripts/comment/CommentsApiTest.java +++ b/source/test-java/org/alfresco/repo/web/scripts/comment/CommentsApiTest.java @@ -18,13 +18,18 @@ */ package org.alfresco.repo.web.scripts.comment; +import java.io.IOException; import java.text.MessageFormat; +import java.util.Date; +import java.util.HashMap; import java.util.List; +import java.util.Map; import javax.transaction.UserTransaction; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; +import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.node.archive.NodeArchiveService; import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.repo.security.authentication.AuthenticationUtil; @@ -35,6 +40,7 @@ import org.alfresco.repo.security.permissions.impl.SimplePermissionEntry; import org.alfresco.repo.security.authentication.MutableAuthenticationDao; import org.alfresco.repo.site.SiteModel; import org.alfresco.repo.web.scripts.BaseWebScriptTest; +import org.alfresco.repo.web.scripts.comments.AbstractCommentsWebScript; import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; @@ -52,10 +58,15 @@ import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.PropertyMap; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; import org.springframework.context.ApplicationContext; import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.TestWebScriptServer.DeleteRequest; import org.springframework.extensions.webscripts.TestWebScriptServer.PostRequest; import org.springframework.extensions.webscripts.TestWebScriptServer.Response; +import org.springframework.extensions.webscripts.WebScriptException; /** * TODO: Fix the loose transaction handling. @@ -63,11 +74,15 @@ import org.springframework.extensions.webscripts.TestWebScriptServer.Response; public class CommentsApiTest extends BaseWebScriptTest { private static final String URL_POST_COMMENT = "api/node/{0}/{1}/{2}/comments"; + private static final String URL_DELETE_COMMENT = "api/comment/node/{0}/{1}/{2}?site={3}&itemtitle={4}&page={5}&pageParams={6}"; private static final String JSON = "application/json"; private static final String SITE_SHORT_NAME = "SomeTestSiteShortName"; private static final String USER_ONE = "SomeTestUserOne"; private static final String USER_TWO = "SomeTestUserTwo"; + private static final String JSON_KEY_NODEREF = "nodeRef"; + private static final String JSON_KEY_ITEM = "item"; + private String requestBodyJson = "{\"title\" : \"Test Title\", \"content\" : \"Test Comment\"}"; private FileFolderService fileFolderService; @@ -212,7 +227,16 @@ public class CommentsApiTest extends BaseWebScriptTest deleteUser(USER_TWO); } - private void addComment(NodeRef nodeRef, String user, int status) throws Exception + /** + * add a comment to given node ref + * + * @param nodeRef + * @param user + * @param status + * @return + * @throws Exception + */ + private Response addComment(NodeRef nodeRef, String user, int status) throws Exception { Response response = null; @@ -222,12 +246,28 @@ public class CommentsApiTest extends BaseWebScriptTest // Not allowed if you're not an admin AuthenticationUtil.setFullyAuthenticatedUser(user); - StringBuilder body = new StringBuilder("{"); - body.append("\"title\" : \"Test Title\", "); - body.append("\"content\" : \"Test Comment\""); - body.append("}"); + // add comment and save response (comment nodeRef) + + StringBuilder body = new StringBuilder("{"); + body.append("\"itemTitle\" : \"Test Title\", "); + body.append("\"content\" : \"Test Comment\", "); + body.append("\"pageParams\" : \"{\\\"nodeRef\\\" : \\\""); + body.append(nodeRef.getStoreRef().getProtocol()); + body.append(":\\/\\/"); + body.append(nodeRef.getStoreRef().getIdentifier()); + body.append("\\/"); + body.append(nodeRef.getId()); + body.append("\\\"}"); + if (nodeRef.equals(sitePage)) + { + body.append("\",\"site\" : \""); + body.append(SITE_SHORT_NAME); + } + body.append("\"}"); + response = sendRequest( + new PostRequest(MessageFormat.format(URL_POST_COMMENT, new Object[] { nodeRef.getStoreRef().getProtocol(), + nodeRef.getStoreRef().getIdentifier(), nodeRef.getId() }), body.toString(), JSON), status); - response = sendRequest(new PostRequest(MessageFormat.format(URL_POST_COMMENT, new Object[] {nodeRef.getStoreRef().getProtocol(), nodeRef.getStoreRef().getIdentifier(), nodeRef.getId()}), body.toString(), JSON), status); assertEquals(status, response.getStatus()); // Normally, webscripts are in their own transaction. The test infrastructure here forces us to have a transaction @@ -240,6 +280,8 @@ public class CommentsApiTest extends BaseWebScriptTest { txn.commit(); } + + return response; } private String getCurrentVersion(NodeRef nodeRef) throws Exception @@ -331,6 +373,51 @@ public class CommentsApiTest extends BaseWebScriptTest assertEquals(modifierBefore, modifierAfter); } + /** + * MNT-15679 + */ + public void testDeleteCommentDoesNotChangeModifiedDate() throws Exception + { + // in site page + permissionService.setPermission(sitePage, USER_TWO, PermissionService.ALL_PERMISSIONS, true); + String modifierBefore = (String) nodeService.getProperty(sitePage, ContentModel.PROP_MODIFIER); + Date modifiedDateBefore = (Date) nodeService.getProperty(sitePage, ContentModel.PROP_MODIFIED); + + Response response1 = addComment(sitePage, USER_TWO, 200); + + JSONObject jsonResponse1 = parseResponseJSON(response1); + String nodeRefComment1 = getOrNull(jsonResponse1, JSON_KEY_NODEREF); + if (nodeRefComment1 != null) + { + NodeRef commentNodeRef1 = new NodeRef(nodeRefComment1); + deleteComment(commentNodeRef1, sitePage, USER_TWO, 200); + } + + Date modifiedDateAfter = (Date) nodeService.getProperty(sitePage, ContentModel.PROP_MODIFIED); + String modifierAfter = (String) nodeService.getProperty(sitePage, ContentModel.PROP_MODIFIER); + assertEquals(modifiedDateBefore.getTime(), modifiedDateAfter.getTime()); + assertEquals(modifierBefore, modifierAfter); + + // in repository - on nodeRef + permissionService.setPermission(nodeRef, USER2, PermissionService.ALL_PERMISSIONS, true); + modifierBefore = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIER); + modifiedDateBefore = (Date) nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIED); + Response response2 = addComment(nodeRef, USER2, 200); + + JSONObject jsonResponse2 = parseResponseJSON(response2); + String nodeRefComment2 = getOrNull(jsonResponse2, JSON_KEY_NODEREF); + if (nodeRefComment2 != null) + { + NodeRef commentNodeRef2 = new NodeRef(nodeRefComment2); + deleteComment(commentNodeRef2, nodeRef, USER2, 200); + } + + modifiedDateAfter = (Date) nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIED); + modifierAfter = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIER); + assertEquals(modifiedDateBefore.getTime(), modifiedDateAfter.getTime()); + assertEquals(modifierBefore, modifierAfter); + } + /** * MNT-12082 */ @@ -388,4 +475,105 @@ public class CommentsApiTest extends BaseWebScriptTest authenticationService.deleteAuthentication(user); } } + + + /** + * delete comment + * + * @param commentNodeRef + * @param parentNodeRef + * @param user + * @param status + * @throws Exception + */ + private void deleteComment(NodeRef commentNodeRef, NodeRef parentNodeRef, String user, int status) throws Exception + { + Response response = null; + + UserTransaction txn = transactionService.getUserTransaction(); + txn.begin(); + + // Not allowed if you're not an admin + AuthenticationUtil.setFullyAuthenticatedUser(user); + + String itemTitle = "Test Title"; + String page = "document-details"; + + StringBuilder pageParamsBuilder = new StringBuilder("{"); + pageParamsBuilder.append("\"nodeRef\" : \""); + pageParamsBuilder.append(parentNodeRef.toString()); + pageParamsBuilder.append("\", "); + pageParamsBuilder.append("}"); + String pageParams = pageParamsBuilder.toString(); + + String URL = MessageFormat.format(URL_DELETE_COMMENT, new Object[] { commentNodeRef.getStoreRef().getProtocol(), + commentNodeRef.getStoreRef().getIdentifier(), commentNodeRef.getId(), SITE_SHORT_NAME, itemTitle, page, pageParams }); + response = sendRequest(new DeleteRequest(URL), status); + assertEquals(status, response.getStatus()); + + // Normally, webscripts are in their own transaction. The test + // infrastructure here forces us to have a transaction + // around the calls. if the WebScript fails, then we should rollback. + if (response.getStatus() == 500) + { + txn.rollback(); + } + else + { + txn.commit(); + } + } + + /** + * returns value from JSON for a given key + * @param json + * @param key + * @return + */ + protected String getOrNull(JSONObject json, String key) + { + if (json != null && json.containsKey(key)) + { + return (String) json.get(key); + } + + JSONObject itemJsonObject = (JSONObject) json.get(JSON_KEY_ITEM); + if (itemJsonObject != null && itemJsonObject.containsKey(key)) + { + return (String) itemJsonObject.get(key); + } + return null; + } + + /** + * parse JSON + * @param response + * @return + */ + protected JSONObject parseResponseJSON(Response response) + { + JSONObject json = null; + String contentType = response.getContentType(); + if (contentType != null && contentType.indexOf(';') != -1) + { + contentType = contentType.substring(0, contentType.indexOf(';')); + } + if (MimetypeMap.MIMETYPE_JSON.equals(contentType)) + { + JSONParser parser = new JSONParser(); + try + { + json = (JSONObject) parser.parse(response.getContentAsString()); + } + catch (IOException io) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid JSON: " + io.getMessage()); + } + catch (ParseException pe) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid JSON: " + pe.getMessage()); + } + } + return json; + } }