Merged HEAD-BUG-FIX (4.3/Cloud) to HEAD (4.3/Cloud)

59349: Merged V4.2-BUG-FIX (4.2.1) to HEAD-BUG-FIX (Cloud/4.3)
      59348: Merged V4.1-BUG-FIX (4.1.8) to V4.2-BUG-FIX (4.2.1)
         59346: MNT-9771: Merged V4.1.7 (4.1.7.1) to V4.1-BUG-FIX (4.1.8)
            59313: MNT-10231 : CLONE: Version History shows incorrect creator for previous versions
               -  Java-backed webscript CommentsPost added. Also auditable behavior is disabled around creating comment-node
            59326: MNT-10231 : CLONE: Version History shows incorrect creator for previous versions
               -  Posting activity item added to CommentsPost web-script. Also copyright message and comments added, @since changed to 4.1.7.1
            59334: MNT-10231: Merged DEV to V4.1.7 (4.1.7.1)
               56938: MNT-9771 : Version History shows incorrect creator for previous versions
                  - Added a test to reproduce this issue


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@62132 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Alan Davis
2014-02-12 01:29:42 +00:00
parent be6f4be740
commit c21e64b37a
4 changed files with 413 additions and 71 deletions

View File

@@ -1,69 +0,0 @@
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/requestutils.lib.js">
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/nodenameutils.lib.js">
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/comments/comments.lib.js">
/**
* Creates a post inside the passed forum node.
*/
function addComment(node)
{
// fetch the data required to create a comment
var title = "";
if (json.has("title"))
{
title = json.get("title");
}
var content = json.get("content");
// fetch the parent to add the node to
var commentsFolder = getOrCreateCommentsFolder(node);
// get a unique name
var name = getUniqueChildName(commentsFolder, "comment");
// create the comment
var commentNode = commentsFolder.createNode(name, "fm:post");
commentNode.mimetype = "text/html";
commentNode.properties.title = title;
commentNode.content = content;
commentNode.save();
return commentNode;
}
function main()
{
// get requested node
var node = getRequestNode();
if (status.getCode() != status.STATUS_OK)
{
return;
}
var comment = addComment(node);
model.item = getCommentData(comment);
model.node = node;
// post an activity item, but only if we've got a site
if (json.has("site") && json.has("itemTitle") && json.has("page"))
{
var siteId = json.get("site");
if ((siteId != null) && (siteId != ""))
{
var params = jsonUtils.toObject(json.get("pageParams")), strParams = "";
for (param in params)
{
strParams += param + "=" + encodeURIComponent(params[param]) + "&";
}
var data =
{
title: json.get("itemTitle"),
page: json.get("page") + (strParams != "" ? "?" + strParams.substring(0, strParams.length - 1) : ""),
nodeRef: node.getNodeRef()
}
activities.postActivity("org.alfresco.comments.comment-created", siteId, "comments", jsonUtils.toJSONString(data));
}
}
}
main();

View File

@@ -1880,7 +1880,17 @@
</list> </list>
</property> </property>
</bean> </bean>
<!-- -->
<!-- CommentService REST API -->
<!-- -->
<bean id="webscript.org.alfresco.repository.comments.comments.post"
class="org.alfresco.repo.web.scripts.comments.CommentsPost"
parent="webscript">
<property name="serviceRegistry" ref="ServiceRegistry"/>
<property name="behaviourFilter" ref="policyBehaviourFilter" />
<property name="activityService" ref="activityService"/>
</bean>
<!-- --> <!-- -->
<!-- Multi-Tenant Admin --> <!-- Multi-Tenant Admin -->

View File

@@ -0,0 +1,371 @@
/*
* Copyright (C) 2005-2013 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 <http://www.gnu.org/licenses/>.
*/
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.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;
/**
* This class is the controller for the comments.post web script.
*
* @author Sergey Scherbovich (based on existing JavaScript webscript controller)
* @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;
@Override
protected Map<String, Object> executeImpl(WebScriptRequest req, Status status, Cache cache)
{
// get requested node
NodeRef nodeRef = parseRequestForNodeRef(req);
// get json object from request
JSONObject json = parseJSON(req);
/* MNT-10231, MNT-9771 fix */
this.behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
try
{
// add a comment
NodeRef commentNodeRef = addComment(nodeRef, json);
// generate response model for a comment node
Map<String, Object> model = generateModel(nodeRef, commentNodeRef);
// post an activity item
postActivity(json, nodeRef);
return model;
}
finally
{
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);
}
catch(Exception e)
{
logger.warn("Error adding comment to activities feed", e);
}
}
}
}
private NodeRef addComment(NodeRef nodeRef, JSONObject json)
{
// fetch the parent to add the node to
NodeRef commentsFolder = getOrCreateCommentsFolder(nodeRef);
// get a unique name
String name = getUniqueChildName("comment");
// create the comment
NodeRef commentNodeRef = nodeService.createNode(commentsFolder,
ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, QName.createValidLocalName(name)),
ForumModel.TYPE_POST).getChildRef();
// fetch the title required to create a comment
String title = getOrNull(json, JSON_KEY_TITLE);
HashMap<QName, Serializable> props = new HashMap<QName, Serializable>(1, 1.0f);
props.put(ContentModel.PROP_TITLE, title != null ? title : "");
nodeService.addProperties(commentNodeRef, props);
ContentWriter writer = contentService.getWriter(commentNodeRef, ContentModel.PROP_CONTENT, true);
// fetch the content of a comment
String contentString = getOrNull(json, JSON_KEY_CONTENT);
writer.setMimetype(MimetypeMap.MIMETYPE_HTML);
writer.putContent(contentString);
return commentNodeRef;
}
private Map<String, Object> generateItemValue(NodeRef commentNodeRef)
{
Map<String, Object> result = new HashMap<String, Object>(4, 1.0f);
String creator = (String)this.nodeService.getProperty(commentNodeRef, ContentModel.PROP_CREATOR);
Serializable created = this.nodeService.getProperty(commentNodeRef, ContentModel.PROP_CREATED);
Serializable modified = this.nodeService.getProperty(commentNodeRef, ContentModel.PROP_MODIFIED);
boolean isUpdated = false;
if (created instanceof Date && modified instanceof Date)
{
isUpdated = ((Date)modified).getTime() - ((Date)created).getTime() > 5000;
}
Serializable owner = this.nodeService.getProperty(commentNodeRef, ContentModel.PROP_OWNER);
String currentUser = this.serviceRegistry.getAuthenticationService().getCurrentUserName();
boolean isSiteManager = this.permissionService.hasPermission(commentNodeRef, SiteModel.SITE_MANAGER) == (AccessStatus.ALLOWED);
boolean isCoordinator = this.permissionService.hasPermission(commentNodeRef, PermissionService.COORDINATOR) == (AccessStatus.ALLOWED);
boolean canEditComment = isSiteManager || isCoordinator || currentUser.equals(creator) || currentUser.equals(owner);
result.put("node", commentNodeRef);
result.put("author", this.personService.getPerson(creator));
result.put("isUpdated", isUpdated);
result.put("canEditComment", canEditComment);
return result;
}
private Map<String, Object> generateModel(NodeRef nodeRef, NodeRef commentNodeRef)
{
Map<String, Object> model = new HashMap<String, Object>(2, 1.0f);
model.put("node", nodeRef);
model.put("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;
}
private NodeRef getOrCreateCommentsFolder(NodeRef nodeRef)
{
NodeRef commentsFolder = getCommentsFolder(nodeRef);
// create a comment folder if it doesn't exist
if (commentsFolder == null)
{
commentsFolder = createCommentsFolder(nodeRef);
}
return commentsFolder;
}
private NodeRef getCommentsFolder(NodeRef nodeRef)
{
if (nodeService.hasAspect(nodeRef, ForumModel.ASPECT_DISCUSSABLE))
{
List<ChildAssociationRef> assocs = nodeService.getChildAssocs(nodeRef, ForumModel.ASSOC_DISCUSSION, RegexQNamePattern.MATCH_ALL);
ChildAssociationRef firstAssoc = assocs.get(0);
return nodeService.getChildByName(firstAssoc.getChildRef(), ContentModel.ASSOC_CONTAINS, COMMENTS_TOPIC_NAME);
}
else
{
return null;
}
}
private NodeRef parseRequestForNodeRef(WebScriptRequest req)
{
Map<String, String> 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();
}
private NodeRef createCommentsFolder(final NodeRef nodeRef)
{
NodeRef commentsFolder = AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<NodeRef>()
{
public NodeRef doWork() throws Exception
{
NodeRef commentsFolder = null;
// ALF-5240: turn off auditing round the discussion node creation to prevent
// the source document from being modified by the first user leaving a comment
behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
try
{
nodeService.addAspect(nodeRef, QName.createQName(NamespaceService.FORUMS_MODEL_1_0_URI, "discussable"), null);
nodeService.addAspect(nodeRef, QName.createQName(NamespaceService.FORUMS_MODEL_1_0_URI, "commentsRollup"), null);
List<ChildAssociationRef> assocs = nodeService.getChildAssocs(nodeRef, QName.createQName(NamespaceService.FORUMS_MODEL_1_0_URI, "discussion"), RegexQNamePattern.MATCH_ALL);
if (assocs.size() != 0)
{
NodeRef forumFolder = assocs.get(0).getChildRef();
Map<QName, Serializable> props = new HashMap<QName, Serializable>(1, 1.0f);
props.put(ContentModel.PROP_NAME, COMMENTS_TOPIC_NAME);
commentsFolder = nodeService.createNode(
forumFolder,
ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, COMMENTS_TOPIC_NAME),
QName.createQName(NamespaceService.FORUMS_MODEL_1_0_URI, "topic"),
props).getChildRef();
}
}
finally
{
behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
}
return commentsFolder;
}
}, AuthenticationUtil.getSystemUserName());
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;
}
}

View File

@@ -12,6 +12,7 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.permissions.PermissionReference; import org.alfresco.repo.security.permissions.PermissionReference;
import org.alfresco.repo.security.permissions.PermissionServiceSPI; import org.alfresco.repo.security.permissions.PermissionServiceSPI;
import org.alfresco.repo.security.permissions.impl.ModelDAO; import org.alfresco.repo.security.permissions.impl.ModelDAO;
import org.alfresco.repo.security.authentication.MutableAuthenticationDao;
import org.alfresco.repo.security.permissions.impl.SimplePermissionEntry; import org.alfresco.repo.security.permissions.impl.SimplePermissionEntry;
import org.alfresco.repo.web.scripts.BaseWebScriptTest; import org.alfresco.repo.web.scripts.BaseWebScriptTest;
import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.model.FileFolderService;
@@ -48,6 +49,7 @@ public class CommentsApiTest extends BaseWebScriptTest
private AuthenticationComponent authenticationComponent; private AuthenticationComponent authenticationComponent;
protected PermissionServiceSPI permissionService; protected PermissionServiceSPI permissionService;
protected ModelDAO permissionModelDAO; protected ModelDAO permissionModelDAO;
private MutableAuthenticationDao authenticationDAO;
private NodeRef rootNodeRef; private NodeRef rootNodeRef;
private NodeRef companyHomeNodeRef; private NodeRef companyHomeNodeRef;
@@ -57,6 +59,9 @@ public class CommentsApiTest extends BaseWebScriptTest
private UserTransaction txn; private UserTransaction txn;
private String USER2 = "user2";
@Override @Override
protected void setUp() throws Exception protected void setUp() throws Exception
{ {
@@ -70,6 +75,7 @@ public class CommentsApiTest extends BaseWebScriptTest
namespaceService = (NamespaceService)appContext.getBean("namespaceService"); namespaceService = (NamespaceService)appContext.getBean("namespaceService");
versionService = (VersionService)appContext.getBean("versionService"); versionService = (VersionService)appContext.getBean("versionService");
personService = (PersonService)getServer().getApplicationContext().getBean("PersonService"); personService = (PersonService)getServer().getApplicationContext().getBean("PersonService");
authenticationDAO = (MutableAuthenticationDao) appContext.getBean("authenticationDao");
authenticationService = (MutableAuthenticationService)getServer().getApplicationContext().getBean("AuthenticationService"); authenticationService = (MutableAuthenticationService)getServer().getApplicationContext().getBean("AuthenticationService");
authenticationComponent = (AuthenticationComponent)getServer().getApplicationContext().getBean("authenticationComponent"); authenticationComponent = (AuthenticationComponent)getServer().getApplicationContext().getBean("authenticationComponent");
permissionService = (PermissionServiceSPI) getServer().getApplicationContext().getBean("permissionService"); permissionService = (PermissionServiceSPI) getServer().getApplicationContext().getBean("permissionService");
@@ -98,6 +104,12 @@ public class CommentsApiTest extends BaseWebScriptTest
nodeRef = fileFolderService.create(companyHomeNodeRef, "Commenty", ContentModel.TYPE_CONTENT).getNodeRef(); nodeRef = fileFolderService.create(companyHomeNodeRef, "Commenty", ContentModel.TYPE_CONTENT).getNodeRef();
versionService.ensureVersioningEnabled(nodeRef, null); versionService.ensureVersioningEnabled(nodeRef, null);
nodeService.setProperty(nodeRef, ContentModel.PROP_AUTO_VERSION_PROPS, true); nodeService.setProperty(nodeRef, ContentModel.PROP_AUTO_VERSION_PROPS, true);
if (authenticationDAO.userExists(USER2))
{
authenticationService.deleteAuthentication(USER2);
}
authenticationService.createAuthentication(USER2, USER2.toCharArray());
createUser(USER_TEST); createUser(USER_TEST);
@@ -154,7 +166,7 @@ public class CommentsApiTest extends BaseWebScriptTest
String version = versionService.getCurrentVersion(nodeRef).getVersionLabel(); String version = versionService.getCurrentVersion(nodeRef).getVersionLabel();
return version; return version;
} }
public void testCommentDoesNotVersion() throws Exception public void testCommentDoesNotVersion() throws Exception
{ {
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
@@ -165,6 +177,10 @@ public class CommentsApiTest extends BaseWebScriptTest
assertEquals(versionBefore, versionAfter); assertEquals(versionBefore, versionAfter);
} }
/**
* MNT-9771
* @throws Exception
*/
public void testCommentPermissions() throws Exception public void testCommentPermissions() throws Exception
{ {
authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName()); authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName());
@@ -217,4 +233,18 @@ public class CommentsApiTest extends BaseWebScriptTest
{ {
return permissionModelDAO.getPermissionReference(null, permission); return permissionModelDAO.getPermissionReference(null, permission);
} }
/**
* MNT-9771
* @throws Exception
*/
public void testCommentDoesNotChangeModifier() throws Exception
{
permissionService.setPermission(nodeRef, USER2, PermissionService.ALL_PERMISSIONS, true);
String modifierBefore = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIER);
addComment(nodeRef, USER2, 200);
String modifierAfter = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIER);
assertEquals(modifierBefore, modifierAfter);
}
} }