diff --git a/l10n.properties b/l10n.properties index 0d33173c8c..ecba98738c 100644 --- a/l10n.properties +++ b/l10n.properties @@ -1,6 +1,6 @@ # Branch specific configuration file for localisation scripts -MESSAGE_SEARCH_PATH="src/main/resources/alfresco/messages/admin-console*.properties src/main/resources/alfresco/messages/custommodel-restapi-messages*.properties src/main/resources/alfresco/messages/rest-framework-messages*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/admin-communitysummary.get*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/consoles/admin-repoconsole.get*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/consoles/admin-tenantconsole.get*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/consoles/admin-workflowconsole.get*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/support-tools/admin-nodebrowser.get*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/audit/entry*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/blogs/post/blog-post.delete*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/links/links-delete.post*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/links/links.post*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/links/links.put*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/person/user-csv-upload.post*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/calendar/event.get*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/calendar/event.post*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/calendar/event.put*.properties" +MESSAGE_SEARCH_PATH="src/main/resources/alfresco/messages/admin-console*.properties src/main/resources/alfresco/messages/custommodel-restapi-messages*.properties src/main/resources/alfresco/messages/rest-framework-messages*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/admin-communitysummary.get*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/consoles/admin-repoconsole.get*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/consoles/admin-tenantconsole.get*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/consoles/admin-workflowconsole.get*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/support-tools/admin-nodebrowser.get*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/audit/entry*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/blogs/post/blog-post.delete*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/links/links-delete.post*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/links/links.post*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/links/links.put*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/person/user-csv-upload.post*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/calendar/event.get*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/calendar/event.post*.properties src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/calendar/event.put*.properties" EXCLUDED_FILES="src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/audit/control.properties" diff --git a/pom.xml b/pom.xml index 27e3c291b4..b894993cab 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,7 @@ ${project.build.directory}/alf_data convert - 7.53 + 7.52 7.9 8.27 1.1 diff --git a/src/main/java/org/alfresco/repo/web/scripts/discussion/AbstractDiscussionWebScript.java b/src/main/java/org/alfresco/repo/web/scripts/discussion/AbstractDiscussionWebScript.java new file mode 100644 index 0000000000..11a4927e03 --- /dev/null +++ b/src/main/java/org/alfresco/repo/web/scripts/discussion/AbstractDiscussionWebScript.java @@ -0,0 +1,603 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.web.scripts.discussion; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.query.PagingRequest; +import org.alfresco.query.PagingResults; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.discussion.DiscussionServiceImpl; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.site.SiteServiceImpl; +import org.alfresco.service.cmr.activities.ActivityService; +import org.alfresco.service.cmr.discussion.DiscussionService; +import org.alfresco.service.cmr.discussion.PostInfo; +import org.alfresco.service.cmr.discussion.TopicInfo; +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.NoSuchPersonException; +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.alfresco.util.Pair; +import org.alfresco.util.ScriptPagingDetails; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.simple.JSONArray; +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; + +/** + * @author Nick Burch + * @since 4.0 + */ +public abstract class AbstractDiscussionWebScript extends DeclarativeWebScript +{ + public static final String DISCUSSIONS_SERVICE_ACTIVITY_APP_NAME = "discussions"; + + /** + * When no maximum or paging info is given, what should we use? + */ + protected static final int MAX_QUERY_ENTRY_COUNT = 1000; + + private static Log logger = LogFactory.getLog(AbstractDiscussionWebScript.class); + + protected static final String KEY_POSTDATA = "postData"; + protected static final String KEY_IS_TOPIC_POST = "isTopicPost"; + protected static final String KEY_TOPIC = "topic"; + protected static final String KEY_POST = "post"; + protected static final String KEY_CAN_EDIT = "canEdit"; + protected static final String KEY_AUTHOR = "author"; + + // Injected services + protected NodeService nodeService; + protected SiteService siteService; + protected PersonService personService; + protected ActivityService activityService; + protected DiscussionService discussionService; + protected PermissionService permissionService; + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setSiteService(SiteService siteService) + { + this.siteService = siteService; + } + + public void setPersonService(PersonService personService) + { + this.personService = personService; + } + + public void setActivityService(ActivityService activityService) + { + this.activityService = activityService; + } + + public void setDiscussionService(DiscussionService discussionService) + { + this.discussionService = discussionService; + } + + public void setPermissionService(PermissionService permissionService) + { + this.permissionService = permissionService; + } + + + protected String getOrNull(JSONObject json, String key) + { + if (json.containsKey(key)) + { + return (String)json.get(key); + } + return null; + } + + /** + * Builds up a listing Paging request, based on the arguments + * specified in the URL + */ + protected PagingRequest buildPagingRequest(WebScriptRequest req) + { + return new ScriptPagingDetails(req, MAX_QUERY_ENTRY_COUNT); + } + + protected List getTags(JSONObject json) + { + List tags = null; + if (json.containsKey("tags")) + { + // Is it "tags":"" or "tags":[...] ? + if (json.get("tags") instanceof String) + { + // This is normally an empty string, skip + String tagsS = (String)json.get("tags"); + if ("".equals(tagsS)) + { + // No tags were given + return null; + } + else + { + // Log, and treat as empty + logger.warn("Unexpected tag data: " + tagsS); + return null; + } + } + else + { + tags = new ArrayList(); + JSONArray jsTags = (JSONArray)json.get("tags"); + for (int i=0; i 0) + { + title = postTitle; + } + } + + try + { + JSONObject params = new JSONObject(); + params.put("topicId", topic.getSystemName()); + + JSONObject activity = new JSONObject(); + activity.put("title", title); + activity.put("page", page + "?topicId=" + topic.getSystemName()); + activity.put("params", params); + + activityService.postActivity( + "org.alfresco.discussions." + thing + "-" + event, + site.getShortName(), + DISCUSSIONS_SERVICE_ACTIVITY_APP_NAME, + activity.toString()); + } + catch(Exception e) + { + // Warn, but carry on + logger.warn("Error adding discussions " + thing + " " + event + " to activities feed", e); + } + } + + /** + * Is the current user allowed to edit this post? + * In order to be deemed allowed, you first need write + * permissions on the underlying node of the post. + * You then also need to either be the cm:creator of + * the post node, or a site manager + */ + protected boolean canUserEditPost(PostInfo post, SiteInfo site) + { + // Are they OK on the node? + AccessStatus canEdit = permissionService.hasPermission(post.getNodeRef(), PermissionService.WRITE); + if (canEdit == AccessStatus.ALLOWED) + { + // Only the creator and site managers may edit + String user = AuthenticationUtil.getFullyAuthenticatedUser(); + if (post.getCreator().equals(user)) + { + // It's their post + return true; + } + if (site != null) + { + String role = siteService.getMembersRole(site.getShortName(), user); + if (SiteServiceImpl.SITE_MANAGER.equals(role)) + { + // Managers may edit + return true; + } + } + } + + // If in doubt, you may not edit + return false; + } + + protected Object buildPerson(String username) + { + // Empty string needed if the user can't be found + Object noSuchPersonResponse = ""; + + if (username == null || username.length() == 0) + { + return noSuchPersonResponse; + } + + try + { + // Will turn into a Script Node needed of the person + NodeRef person = personService.getPerson(username); + return person; + } + catch(NoSuchPersonException e) + { + // This is normally caused by the person having been deleted + return noSuchPersonResponse; + } + } + + /* + * Was topicpost.lib.js getReplyPostData + * + * TODO Switch the FTL to prefer the Info object rather than the ScriptNode + */ + protected Map renderPost(PostInfo post, SiteInfo site) + { + Map item = new HashMap(); + item.put(KEY_IS_TOPIC_POST, false); + item.put(KEY_POST, post.getNodeRef()); + item.put(KEY_CAN_EDIT, canUserEditPost(post, site)); + item.put(KEY_AUTHOR, buildPerson(post.getCreator())); + return item; + } + + /* + * Was topicpost.lib.js getTopicPostData / getTopicPostDataFromTopicAndPosts + * + * TODO Switch the FTL to prefer the Info object rather than the ScriptNode + */ + protected Map renderTopic(TopicInfo topic, SiteInfo site) + { + // Fetch the primary post + PostInfo primaryPost = discussionService.getPrimaryPost(topic); + if (primaryPost == null) + { + throw new WebScriptException(Status.STATUS_PRECONDITION_FAILED, + "First (primary) post was missing from the topic, can't fetch"); + } + + // Fetch the most recent reply + PostInfo mostRecentPost = discussionService.getMostRecentPost(topic); + + // Find out how many replies there are + int numReplies; + if (mostRecentPost.getNodeRef().equals( primaryPost.getNodeRef() )) + { + // Only the one post in the topic + mostRecentPost = null; + numReplies = 0; + } + else + { + // Use this trick to get the number of posts in the topic, + // but without needing to get lots of data and objects + PagingRequest paging = new PagingRequest(1); + paging.setRequestTotalCountMax(MAX_QUERY_ENTRY_COUNT); + PagingResults posts = discussionService.listPosts(topic, paging); + + // The primary post is in the list, so exclude from the reply count + numReplies = posts.getTotalResultCount().getFirst() - 1; + } + + // Build the details + Map item = new HashMap(); + item.put(KEY_IS_TOPIC_POST, true); + item.put(KEY_TOPIC, topic.getNodeRef()); + item.put(KEY_POST, primaryPost.getNodeRef()); + item.put(KEY_CAN_EDIT, canUserEditPost(primaryPost, site)); + item.put(KEY_AUTHOR, buildPerson(topic.getCreator())); + + // The reply count is one less than all posts (first is the primary one) + item.put("totalReplyCount", numReplies); + + // Add the topic site + item.put("site", topic.getShortSiteName()); + + // We want details on the most recent post + if (mostRecentPost != null) + { + item.put("lastReply", mostRecentPost.getNodeRef()); + item.put("lastReplyBy", buildPerson(mostRecentPost.getCreator())); + } + + // Include the tags + item.put("tags", topic.getTags()); + + // All done + return item; + } + + /* + * Renders out the list of topics + * TODO Fetch the post data in one go, rather than one at a time + */ + protected Map renderTopics(PagingResults topics, + PagingRequest paging, SiteInfo site) + { + return renderTopics(topics.getPage(), topics.getTotalResultCount(), paging, site); + } + /* + * Renders out the list of topics + * TODO Fetch the post data in one go, rather than one at a time + */ + protected Map renderTopics(List topics, + Pair size, PagingRequest paging, SiteInfo site) + { + Map model = new HashMap(); + + // Paging info + model.put("total", size.getFirst()); + model.put("pageSize", paging.getMaxItems()); + model.put("startIndex", paging.getSkipCount()); + model.put("itemCount", topics.size()); + + // Data + List> items = new ArrayList>(); + for (TopicInfo topic : topics) + { + // ACE-772 fix of incorrect display of topics into "My Discussions" dashlet. + // Into "My Discussions" dashlet forum topic will be displayed only if user is a member of that site. + if (null == site && null != topic.getShortSiteName()) + { + String currentUser = AuthenticationUtil.getFullyAuthenticatedUser(); + String siteShortName = topic.getShortSiteName(); + boolean isSiteMember = siteService.isMember(siteShortName, currentUser); + + if (isSiteMember) + { + items.add(renderTopic(topic, site)); + } + } + // Display all topics on the forum of the site. + else + { + items.add(renderTopic(topic, site)); + } + } + model.put("items", items); + + // All done + return model; + } + + protected Map buildCommonModel(SiteInfo site, TopicInfo topic, + PostInfo post, WebScriptRequest req) + { + // Build the common model parts + Map model = new HashMap(); + model.put(KEY_TOPIC, topic); + model.put(KEY_POST, post); + + // Capture the site details only if site based + if (site != null) + { + model.put("siteId", site.getShortName()); + model.put("site", site); + } + + // The limit on the length of the content to be returned + int contentLength = -1; + String contentLengthS = req.getParameter("contentLength"); + if (contentLengthS != null) + { + try + { + contentLength = Integer.parseInt(contentLengthS); + } + catch (NumberFormatException e) + { + logger.info("Skipping invalid length " + contentLengthS); + } + } + model.put("contentLength", contentLength); + + // All done + return model; + } + + @Override + protected Map executeImpl(WebScriptRequest req, + Status status, Cache cache) + { + Map templateVars = req.getServiceMatch().getTemplateVars(); + if (templateVars == null) + { + String error = "No parameters supplied"; + throw new WebScriptException(Status.STATUS_BAD_REQUEST, error); + } + + + // Parse the JSON, if supplied + 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()); + } + } + + + // Did they request it by node reference or site? + NodeRef nodeRef = null; + SiteInfo site = null; + TopicInfo topic = null; + PostInfo post = null; + + if (templateVars.containsKey("site")) + { + // Site, and optionally topic + String siteName = templateVars.get("site"); + site = siteService.getSite(siteName); + if (site == null) + { + String error = "Could not find site: " + siteName; + throw new WebScriptException(Status.STATUS_NOT_FOUND, error); + } + + // Did they give a topic name too? + if (templateVars.containsKey("path")) + { + String name = templateVars.get("path"); + topic = discussionService.getTopic(site.getShortName(), name); + + if (topic == null) + { + String error = "Could not find topic '" + name + "' for site '" + + site.getShortName() + "'"; + throw new WebScriptException(Status.STATUS_NOT_FOUND, error); + } + nodeRef = topic.getNodeRef(); + } + else + { + // The NodeRef is the container (if it exists) + if (siteService.hasContainer(siteName, DiscussionServiceImpl.DISCUSSION_COMPONENT)) + { + nodeRef = siteService.getContainer(siteName, DiscussionServiceImpl.DISCUSSION_COMPONENT); + } + } + } + else if (templateVars.containsKey("store_type") && + templateVars.containsKey("store_id") && + templateVars.containsKey("id")) + { + // NodeRef, normally Topic or Discussion + StoreRef store = new StoreRef( + templateVars.get("store_type"), + templateVars.get("store_id")); + + nodeRef = new NodeRef(store, templateVars.get("id")); + if (! nodeService.exists(nodeRef)) + { + String error = "Could not find node: " + nodeRef; + throw new WebScriptException(Status.STATUS_NOT_FOUND, error); + } + + // Try to build the appropriate object for it + Pair objects = discussionService.getForNodeRef(nodeRef); + if (objects != null) + { + topic = objects.getFirst(); + post = objects.getSecond(); + } + + // See if it's actually attached to a site + if (topic != null) + { + NodeRef container = topic.getContainerNodeRef(); + if (container != null) + { + NodeRef maybeSite = nodeService.getPrimaryParent(container).getParentRef(); + if (maybeSite != null) + { + // Try to make it a site, will return Null if it isn't one + site = siteService.getSite(maybeSite); + } + } + } + } + else + { + String error = "Unsupported template parameters found"; + throw new WebScriptException(Status.STATUS_BAD_REQUEST, error); + } + + // Have the real work done + return executeImpl(site, nodeRef, topic, post, req, json, status, cache); + } + + protected abstract Map executeImpl(SiteInfo site, + NodeRef nodeRef, TopicInfo topic, PostInfo post, + WebScriptRequest req, JSONObject json, Status status, Cache cache); + +} diff --git a/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumPostDelete.java b/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumPostDelete.java new file mode 100644 index 0000000000..68f3350b0c --- /dev/null +++ b/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumPostDelete.java @@ -0,0 +1,125 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.web.scripts.discussion; + +import java.text.MessageFormat; +import java.util.Map; +import java.util.ResourceBundle; + +import org.alfresco.service.cmr.discussion.PostInfo; +import org.alfresco.service.cmr.discussion.TopicInfo; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.site.SiteInfo; +import org.json.simple.JSONObject; +import org.springframework.extensions.webscripts.Cache; +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 forum post deleting forum-post.delete webscript. + * + * @author Nick Burch + * @since 4.0 + */ +public class ForumPostDelete extends AbstractDiscussionWebScript +{ + private static final String MSG_NODE_MARKED_REMOVED = "forum-post.msg.marked.removed"; + private static final String MSG_NODE_DELETED = "forum-post.msg.deleted"; + private static final String DELETED_POST_TEXT = "[[deleted]]"; + + @Override + protected Map executeImpl(SiteInfo site, NodeRef nodeRef, + TopicInfo topic, PostInfo post, WebScriptRequest req, JSONObject json, + Status status, Cache cache) + { + final ResourceBundle rb = getResources(); + + // Build the common model parts + Map model = buildCommonModel(site, topic, post, req); + + + // Are we deleting a topic, or a post in it? + String message = null; + if (post != null) + { + message = doDeletePost(topic, post, rb); + } + else if (topic != null) + { + message = doDeleteTopic(topic, site, req, json, rb); + } + else + { + String error = "Node was of the wrong type, only Topic and Post are supported"; + throw new WebScriptException(Status.STATUS_BAD_REQUEST, error); + } + + + // Finish the model and return + model.put("message", message); + return model; + } + + private String doDeleteTopic(TopicInfo topic, SiteInfo site, + WebScriptRequest req, JSONObject json, ResourceBundle rb) + { + // Delete the topic, which removes all its posts too + discussionService.deleteTopic(topic); + + // Add an activity entry for this if it's site based + if (site != null) + { + addActivityEntry("post", "deleted", topic, null, site, req, json); + } + + // All done + String message = rb.getString(MSG_NODE_DELETED); + + return MessageFormat.format(message, topic.getNodeRef()); + } + + /** + * We can't just delete posts with replies attached to them, + * as that breaks the reply threading. + * For that reason, we mark deleted posts with a special + * text contents. + * TODO If a post has no replies, then delete it fully + */ + private String doDeletePost(TopicInfo topic, PostInfo post, ResourceBundle rb) + { + // Set the marker text and save + post.setTitle(DELETED_POST_TEXT); + post.setContents(DELETED_POST_TEXT); + discussionService.updatePost(post); + + // Note - we don't add activity feed entries for deleted posts + // Only deleted whole topic qualify for that at the moment + + String message = rb.getString(MSG_NODE_MARKED_REMOVED); + return MessageFormat.format(message, post.getNodeRef()); + } +} diff --git a/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumPostGet.java b/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumPostGet.java new file mode 100644 index 0000000000..03ee838c19 --- /dev/null +++ b/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumPostGet.java @@ -0,0 +1,74 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.web.scripts.discussion; + +import java.util.Map; + +import org.alfresco.service.cmr.discussion.PostInfo; +import org.alfresco.service.cmr.discussion.TopicInfo; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.site.SiteInfo; +import org.json.simple.JSONObject; +import org.springframework.extensions.webscripts.Cache; +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 discussions page fetching forum-post.get webscript. + * + * @author Nick Burch + * @since 4.0 + */ +public class ForumPostGet extends AbstractDiscussionWebScript +{ + @Override + protected Map executeImpl(SiteInfo site, NodeRef nodeRef, + TopicInfo topic, PostInfo post, WebScriptRequest req, JSONObject json, + Status status, Cache cache) + { + // Build the common model parts + Map model = buildCommonModel(site, topic, post, req); + + // Did they want just one post, or the whole of the topic? + if (post != null) + { + model.put(KEY_POSTDATA, renderPost(post, site)); + } + else if (topic != null) + { + model.put(KEY_POSTDATA, renderTopic(topic, site)); + } + else + { + String error = "Node was of the wrong type, only Topic and Post are supported"; + throw new WebScriptException(Status.STATUS_BAD_REQUEST, error); + } + + // All done + return model; + } +} diff --git a/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumPostPut.java b/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumPostPut.java new file mode 100644 index 0000000000..1b53e97b16 --- /dev/null +++ b/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumPostPut.java @@ -0,0 +1,140 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.web.scripts.discussion; + +import java.util.List; +import java.util.Map; + +import org.alfresco.service.cmr.discussion.PostInfo; +import org.alfresco.service.cmr.discussion.TopicInfo; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.site.SiteInfo; +import org.json.simple.JSONObject; +import org.springframework.extensions.webscripts.Cache; +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 discussions page editing forum-post.put webscript. + * + * @author Nick Burch + * @since 4.0 + */ +public class ForumPostPut extends AbstractDiscussionWebScript +{ + @Override + protected Map executeImpl(SiteInfo site, NodeRef nodeRef, + TopicInfo topic, PostInfo post, WebScriptRequest req, JSONObject json, + Status status, Cache cache) + { + // Build the common model parts + Map model = buildCommonModel(site, topic, post, req); + + // Did they want to change a reply or the whole topic? + if (post != null) + { + // Update the specified post + doUpdatePost(post, post.getTopic(), req, json); + + // Add the activity entry for the reply change + addActivityEntry("reply", "updated", post.getTopic(), post, site, req, json); + + // Build the JSON for just this post + model.put(KEY_POSTDATA, renderPost(post, site)); + } + else if (topic != null) + { + // Update the primary post of the topic + post = discussionService.getPrimaryPost(topic); + if (post == null) + { + throw new WebScriptException(Status.STATUS_PRECONDITION_FAILED, + "First (primary) post was missing from the topic, can't fetch"); + } + doUpdatePost(post, topic, req, json); + + // Add the activity entry for the topic change + addActivityEntry("post", "updated", topic, null, site, req, json); + + // Build the JSON for the whole topic + model.put(KEY_POSTDATA, renderTopic(topic, site)); + } + else + { + String error = "Node was of the wrong type, only Topic and Post are supported"; + throw new WebScriptException(Status.STATUS_BAD_REQUEST, error); + } + + // All done + return model; + } + + private void doUpdatePost(PostInfo post, TopicInfo topic, WebScriptRequest req, + JSONObject json) + { + boolean updateTopic = false; + // Fetch the details from the JSON + + // Update the titles on the post and it's topic + if (json.containsKey("title")) + { + String title = (String)json.get("title"); + post.setTitle(title); + if (title.length() > 0) + { + updateTopic = true; + topic.setTitle(title); + } + } + + // Contents is on the post + if (json.containsKey("content")) + { + post.setContents((String)json.get("content")); + } + + // Tags are on the topic + if (json.containsKey("tags")) + { + topic.getTags().clear(); + + List tags = getTags(json); + if (tags != null) + { + topic.getTags().addAll(tags); + } + updateTopic = true; + } + + // Save the topic and the post + if (updateTopic == true) + { + discussionService.updateTopic(topic); + } + discussionService.updatePost(post); + } +} diff --git a/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumPostRepliesGet.java b/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumPostRepliesGet.java new file mode 100644 index 0000000000..35718fc934 --- /dev/null +++ b/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumPostRepliesGet.java @@ -0,0 +1,111 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.web.scripts.discussion; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.alfresco.service.cmr.discussion.PostInfo; +import org.alfresco.service.cmr.discussion.PostWithReplies; +import org.alfresco.service.cmr.discussion.TopicInfo; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.site.SiteInfo; +import org.json.simple.JSONObject; +import org.springframework.extensions.webscripts.Cache; +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 discussions page creating forum-post-replies.get webscript. + * + * @author Nick Burch + * @since 4.0 + */ +public class ForumPostRepliesGet extends AbstractDiscussionWebScript +{ + @Override + protected Map executeImpl(SiteInfo site, NodeRef nodeRef, + TopicInfo topic, PostInfo post, WebScriptRequest req, JSONObject json, + Status status, Cache cache) + { + // How many levels did they want? + int levels = 1; + String levelsS = req.getParameter("levels"); + if (levelsS != null) + { + try + { + levels = Integer.parseInt(levelsS); + } + catch (NumberFormatException e) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Level depth parameter invalid"); + } + } + + // Fetch the replies + PostWithReplies replies; + if (post != null) + { + replies = discussionService.listPostReplies(post, levels); + } + else if (topic != null) + { + replies = discussionService.listPostReplies(topic, levels); + } + else + { + String error = "Node was of the wrong type, only Topic and Post are supported"; + throw new WebScriptException(Status.STATUS_BAD_REQUEST, error); + } + + // Build the common model parts + Map model = buildCommonModel(site, topic, post, req); + + // Build the JSON for the replies + model.put("data", renderReplies(replies, site).get("children")); + + // All done + return model; + } + + private Map renderReplies(PostWithReplies replies, SiteInfo site) + { + Map reply = renderPost(replies.getPost(), site); + reply.put("childCount", replies.getReplies().size()); + + List> r = new ArrayList>(); + for (PostWithReplies child : replies.getReplies()) + { + r.add(renderReplies(child, site)); + } + reply.put("children", r); + + return reply; + } +} diff --git a/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumPostRepliesPost.java b/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumPostRepliesPost.java new file mode 100644 index 0000000000..a0d3e38247 --- /dev/null +++ b/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumPostRepliesPost.java @@ -0,0 +1,117 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.web.scripts.discussion; + +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.discussion.PostInfo; +import org.alfresco.service.cmr.discussion.TopicInfo; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.site.SiteInfo; +import org.json.simple.JSONObject; +import org.springframework.extensions.webscripts.Cache; +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 discussions page creating forum-post-replies.post webscript. + * + * @author Nick Burch + * @since 4.0 + */ +public class ForumPostRepliesPost extends AbstractDiscussionWebScript +{ + @Override + protected Map executeImpl(SiteInfo site, NodeRef nodeRef, + TopicInfo topic, PostInfo post, WebScriptRequest req, JSONObject json, + Status status, Cache cache) + { + // If they're trying to create a reply to a topic, they actually + // mean to create the reply on the primary post + if (post == null) + { + post = discussionService.getPrimaryPost(topic); + if (post == null) + { + throw new WebScriptException(Status.STATUS_PRECONDITION_FAILED, + "First (primary) post was missing from the topic, can't fetch"); + } + } + else if (topic == null) + { + String error = "Node was of the wrong type, only Topic and Post are supported"; + throw new WebScriptException(Status.STATUS_BAD_REQUEST, error); + } + + // Have the reply created + PostInfo reply = doCreatePost(post, topic, req, json); + + // Add the activity entry for the reply change + addActivityEntry("reply", "created", topic, reply, site, req, json); + + // Build the common model parts + Map model = buildCommonModel(site, topic, reply, req); + + // Build the JSON for the new reply post + model.put(KEY_POSTDATA, renderPost(reply, site)); + + // All done + return model; + } + + private PostInfo doCreatePost(PostInfo post, TopicInfo topic, WebScriptRequest req, + JSONObject json) + { + // Fetch the details from the JSON + String title = null; + if (json.containsKey("title")) + { + title = (String)json.get("title"); + } + + String contents = null; + if (json.containsKey("content")) + { + contents = (String)json.get("content"); + } + + + // Create the reply + PostInfo reply = discussionService.createReply(post, contents); + + // Set the title if needed (it normally isn't) + if (title != null && title.length() > 0) + { + nodeService.setProperty(reply.getNodeRef(), ContentModel.PROP_TITLE, title); + reply = discussionService.getPost(topic, reply.getSystemName()); + } + + // All done + return reply; + } +} diff --git a/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumTopicPost.java b/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumTopicPost.java new file mode 100644 index 0000000000..6bbe4394d7 --- /dev/null +++ b/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumTopicPost.java @@ -0,0 +1,110 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.web.scripts.discussion; + +import java.util.List; +import java.util.Map; + +import org.alfresco.service.cmr.discussion.PostInfo; +import org.alfresco.service.cmr.discussion.TopicInfo; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.site.SiteInfo; +import org.json.simple.JSONObject; +import org.springframework.extensions.webscripts.Cache; +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 discussions page editing forum-posts.post webscript. + * + * @author Nick Burch + * @since 4.0 + */ +public class ForumTopicPost extends AbstractDiscussionWebScript +{ + @Override + protected Map executeImpl(SiteInfo site, NodeRef nodeRef, + TopicInfo topic, PostInfo post, WebScriptRequest req, JSONObject json, + Status status, Cache cache) + { + // They shouldn't be adding to an existing Post or Topic + if (topic != null || post != null) + { + String error = "Can't create a new Topic inside an existing Topic or Post"; + throw new WebScriptException(Status.STATUS_BAD_REQUEST, error); + } + + + // Grab the details of the new Topic and Post + String title = ""; + String contents = ""; + if (json.containsKey("title")) + { + title = (String)json.get("title"); + } + if (json.containsKey("content")) + { + contents = (String)json.get("content"); + } + List tags = getTags(json); + + + // Have the topic created + if (site != null) + { + topic = discussionService.createTopic(site.getShortName(), title); + } + else + { + topic = discussionService.createTopic(nodeRef, title); + } + if (tags != null && tags.size() > 0) + { + topic.getTags().clear(); + topic.getTags().addAll(tags); + discussionService.updateTopic(topic); + } + + + // Have the primary post created + post = discussionService.createPost(topic, contents); + + + // Record the activity + addActivityEntry("post", "created", topic, post, site, req, json); + + + // Build the common model parts + Map model = buildCommonModel(site, topic, post, req); + + // Build the JSON for the whole topic + model.put(KEY_POSTDATA, renderTopic(topic, site)); + + // All done + return model; + } +} diff --git a/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumTopicsFilteredGet.java b/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumTopicsFilteredGet.java new file mode 100644 index 0000000000..a4e9c40649 --- /dev/null +++ b/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumTopicsFilteredGet.java @@ -0,0 +1,414 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.web.scripts.discussion; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Comparator; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import org.alfresco.query.EmptyPagingResults; +import org.alfresco.query.PagingRequest; +import org.alfresco.query.PagingResults; +import org.alfresco.repo.discussion.TopicInfoImpl; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.discussion.PostInfo; +import org.alfresco.service.cmr.discussion.TopicInfo; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.LimitBy; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.ResultSetRow; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.util.ISO9075; +import org.alfresco.util.Pair; +import org.alfresco.util.ScriptPagingDetails; +import org.json.simple.JSONObject; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * Gets topics matching the filters passed to it in the URL. + * + * topics = 'mine' (searches for posts by the user) or 'all' (ignores the author in the search) + * history = days in the past to search + * resultSize = the number of topics returned in the results + * + * @author Jamie Allison + */ +public class ForumTopicsFilteredGet extends AbstractDiscussionWebScript +{ + //Filter Defaults + protected static final String DEFAULT_TOPIC_AUTHOR = "mine"; + protected static final int DEFAULT_TOPIC_LATEST_POST_DAYS_AGO = 1; + protected static final int DEFAULT_MAX_RESULTS = 10; + + protected static final StoreRef SPACES_STORE = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore"); + + protected static final String SEARCH_QUERY = "TYPE:\"{http://www.alfresco.org/model/forum/1.0}post\"" + + " AND PATH:\"/app:company_home/st:sites/%s/cm:discussions/*/*\"" + + " AND @cm:created:[\"%s\" TO NOW]"; + + /** Spring-injected services */ + private SearchService searchService; + + /** + * Sets the searchService. + * + * @param searchService SearchService + */ + public void setSearchService(SearchService searchService) + { + this.searchService = searchService; + } + + /** + * Overrides AbstractDiscussionWebScript to allow a null site + * + * @param req WebScriptRequest + * @param status Status + * @param cache Cache + * + * @return Map + */ + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + Map templateVars = req.getServiceMatch().getTemplateVars(); + if (templateVars == null) + { + String error = "No parameters supplied"; + throw new WebScriptException(Status.STATUS_BAD_REQUEST, error); + } + + SiteInfo site = null; + + if (templateVars.containsKey("site")) + { + // Site, and optionally topic + String siteName = templateVars.get("site"); + site = siteService.getSite(siteName); + if (site == null) + { + String error = "Could not find site: " + siteName; + throw new WebScriptException(Status.STATUS_NOT_FOUND, error); + } + } + + // Have the real work done + return executeImpl(site, null, null, null, req, null, null, null); + } + + /** + * @param site SiteInfo + * @param nodeRef Not required. It is only included because it is overriding the parent class. + * @param topic Not required. It is only included because it is overriding the parent class. + * @param post Not required. It is only included because it is overriding the parent class. + * @param req WebScriptRequest + * @param status Not required. It is only included because it is overriding the parent class. + * @param cache Not required. It is only included because it is overriding the parent class. + * + * @return Map + */ + @Override + protected Map executeImpl(SiteInfo site, NodeRef nodeRef, TopicInfo topic, + PostInfo post, WebScriptRequest req, JSONObject json, Status status, Cache cache) + { + // They shouldn't be trying to list of an existing Post or Topic + if (topic != null || post != null) + { + String error = "Can't list Topics inside an existing Topic or Post"; + throw new WebScriptException(Status.STATUS_BAD_REQUEST, error); + } + + // Set search filter to users topics or all topics + String pAuthor = req.getParameter("topics"); + String author = DEFAULT_TOPIC_AUTHOR; + if (pAuthor != null) + { + author = pAuthor; + } + // Set the number of days in the past to search from + String pDaysAgo = req.getParameter("history"); + int daysAgo = DEFAULT_TOPIC_LATEST_POST_DAYS_AGO; + if (pDaysAgo != null) + { + try + { + daysAgo = Integer.parseInt(pDaysAgo); + } + catch (NumberFormatException e) + { + //do nothing. history has already been preset to the default value. + } + } + + // Get the complete search query + Pair searchQuery = getSearchQuery(site, author, daysAgo); + + // Get the filtered topics + PagingRequest paging = buildPagingRequest(req); + PagingResults topics = doSearch(searchQuery, false, paging); + + // Build the common model parts + Map model = buildCommonModel(site, topic, post, req); + + // Have the topics rendered + model.put("data", renderTopics(topics, paging, site)); + + // All done + return model; + } + + /** + * Do the actual search + * + * @param searchQuery Pair with query string in first and query language in second + * @param sortAscending boolean + * @param paging PagingRequest + */ + protected PagingResults doSearch(Pair searchQuery, boolean sortAscending, PagingRequest paging) + { + ResultSet resultSet = null; + PagingResults pagedResults = new EmptyPagingResults(); + + String sortOn = "@{http://www.alfresco.org/model/content/1.0}created"; + + // Setup the search parameters + SearchParameters sp = new SearchParameters(); + sp.addStore(SPACES_STORE); + sp.setQuery(searchQuery.getFirst()); + sp.setLanguage(searchQuery.getSecond()); + sp.addSort(sortOn, sortAscending); + if (paging.getMaxItems() > 0) + { + //Multiply maxItems by 10. This is to catch topics that have multiple replies and ensure that the maximum number of topics is shown. + sp.setLimit(paging.getMaxItems()*10); + sp.setLimitBy(LimitBy.FINAL_SIZE); + } + if (paging.getSkipCount() > 0) + { + sp.setSkipCount(paging.getSkipCount()); + } + + try + { + resultSet = searchService.query(sp); + pagedResults = wrap(resultSet, paging); + } + finally + { + try + { + resultSet.close(); + } + catch(Exception e) + { + //do nothing + } + } + + return pagedResults; + } + + /** + * Build the search query from the passed in parameters and SEARCH_QUERY constant + * + * @param site SiteInfo + * @param author String + * @param daysAgo int + * @return Pair with the query string in first and query language in second + */ + protected Pair getSearchQuery(SiteInfo site, String author, int daysAgo) + { + String search = String.format(SEARCH_QUERY, + (site != null ? "cm:" + ISO9075.encode(site.getShortName()) : "*"), + getDateXDaysAgo(daysAgo) + ); + + // If author equals 'mine' add cm:creator to the search query otherwise leave out + if(author.equals(DEFAULT_TOPIC_AUTHOR)) + { + search += " AND @cm:creator:\"" + AuthenticationUtil.getFullyAuthenticatedUser() + "\""; + } + + // Add the query string and language to the returned results + Pair searchQuery = new Pair(search, SearchService.LANGUAGE_FTS_ALFRESCO); + + return searchQuery; + } + + /** + * Get the date x days ago in the format 'yyyy-MM-dd' + * + * @param daysAgo int + * @return String + */ + protected String getDateXDaysAgo(int daysAgo) + { + Calendar calendar = Calendar.getInstance(); + calendar.add(Calendar.DAY_OF_MONTH, (daysAgo * -1)); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + + return sdf.format(calendar.getTime()); + } + + /** + * Builds up a listing Paging request, based on the arguments specified in the URL + * + * @param req WebScriptRequest + * @return PagingRequest + */ + @Override + protected PagingRequest buildPagingRequest(WebScriptRequest req) + { + // Grab the number of topics to return + String pResultSize = req.getParameter("resultSize"); + int resultSize = DEFAULT_MAX_RESULTS; + if (pResultSize != null) + { + try + { + resultSize = Integer.parseInt(pResultSize); + } + catch (NumberFormatException e) + { + //do nothing. ResultSize has already been preset to the default value. + } + } + return new ScriptPagingDetails(req, resultSize); + } + + /** + * Wrap up search results as {@link TopicInfo} instances + * + * @param finalResults ResultSet + * @param paging PagingRequest + */ + protected PagingResults wrap(final ResultSet finalResults, PagingRequest paging) + { + int maxItems = paging.getMaxItems(); + Comparator lastPostDesc = new Comparator() + { + @Override + public int compare(TopicInfo t1, TopicInfo t2) + { + Date t1LastPostDate = t1.getCreatedAt(); + if(discussionService.getMostRecentPost(t1) != null) + { + t1LastPostDate = discussionService.getMostRecentPost(t1).getCreatedAt(); + } + + Date t2LastPostDate = t2.getCreatedAt(); + if(discussionService.getMostRecentPost(t2) != null) + { + t2LastPostDate = discussionService.getMostRecentPost(t2).getCreatedAt(); + } + return t2LastPostDate.compareTo(t1LastPostDate); + } + }; + + final Set topics = new TreeSet(lastPostDesc); + + for (ResultSetRow row : finalResults) + { + Pair pair = discussionService.getForNodeRef(row.getNodeRef()); + TopicInfo topic = pair.getFirst(); + if(topic != null) + { + String path = nodeService.getPath(topic.getNodeRef()).toDisplayPath(nodeService, permissionService); + String site = path.split("/")[3]; + TopicInfoImpl tii = (TopicInfoImpl)topic; + tii.setShortSiteName(site); + topics.add(tii); + + if(topics.size() >= maxItems) + { + break; + } + } + } + + // Wrap + return new PagingResults() + { + @Override + public boolean hasMoreItems() + { + try + { + return finalResults.hasMore(); + } + catch(UnsupportedOperationException e) + { + // Not all search results support paging + return false; + } + } + + @Override + public Pair getTotalResultCount() + { + int skipCount = 0; + int itemsRemainingAfterThisPage = 0; + try + { + skipCount = finalResults.getStart(); + } + catch(UnsupportedOperationException e) {} + try + { + itemsRemainingAfterThisPage = finalResults.length(); + } + catch(UnsupportedOperationException e) {} + + final int totalItemsInUnpagedResultSet = skipCount + itemsRemainingAfterThisPage; + return new Pair(totalItemsInUnpagedResultSet, totalItemsInUnpagedResultSet); + } + + @Override + public List getPage() + { + return new ArrayList(topics); + } + + @Override + public String getQueryExecutionId() + { + return null; + } + }; + } +} diff --git a/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumTopicsGet.java b/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumTopicsGet.java new file mode 100644 index 0000000000..662e54f1c4 --- /dev/null +++ b/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumTopicsGet.java @@ -0,0 +1,121 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.web.scripts.discussion; + +import java.util.Map; + +import org.alfresco.query.PagingRequest; +import org.alfresco.query.PagingResults; +import org.alfresco.service.cmr.discussion.PostInfo; +import org.alfresco.service.cmr.discussion.TopicInfo; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.site.SiteInfo; +import org.json.simple.JSONObject; +import org.springframework.extensions.surf.util.URLDecoder; +import org.springframework.extensions.webscripts.Cache; +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 discussions topics fetching forum-posts.get webscript. + * + * @author Nick Burch + * @since 4.0 + */ +public class ForumTopicsGet extends AbstractDiscussionWebScript +{ + @Override + protected Map executeImpl(SiteInfo site, NodeRef nodeRef, + TopicInfo topic, PostInfo post, WebScriptRequest req, JSONObject json, + Status status, Cache cache) + { + // They shouldn't be trying to list of an existing Post or Topic + if (topic != null || post != null) + { + String error = "Can't list Topics inside an existing Topic or Post"; + throw new WebScriptException(Status.STATUS_BAD_REQUEST, error); + } + + // Do we need to list or search? + boolean tagSearch = false; + String tag = req.getParameter("tag"); + if (tag != null && tag.length() > 0) + { + tagSearch = true; + + // Tags can be full unicode strings, so decode + tag = URLDecoder.decode(tag); + } + + // Get the topics + boolean oldestTopicsFirst = false; + PagingResults topics = null; + PagingRequest paging = buildPagingRequest(req); + if (tagSearch) + { + // Tag based is a search rather than a listing + if (site != null) + { + topics = discussionService.findTopics(site.getShortName(), null, tag, oldestTopicsFirst, paging); + } + else + { + topics = discussionService.findTopics(nodeRef, null, tag, oldestTopicsFirst, paging); + } + } + else + { + if (site != null) + { + topics = discussionService.listTopics(site.getShortName(), oldestTopicsFirst, paging); + } + else + { + topics = discussionService.listTopics(nodeRef, oldestTopicsFirst, buildPagingRequest(req)); + } + } + + + // If they did a site based search, and the component hasn't + // been created yet, use the site for the permissions checking + if (site != null && nodeRef == null) + { + nodeRef = site.getNodeRef(); + } + + + // Build the common model parts + Map model = buildCommonModel(site, topic, post, req); + model.put("forum", nodeRef); + + // Have the topics rendered + model.put("data", renderTopics(topics, paging, site)); + + // All done + return model; + } +} diff --git a/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumTopicsHotGet.java b/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumTopicsHotGet.java new file mode 100644 index 0000000000..579bf91a50 --- /dev/null +++ b/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumTopicsHotGet.java @@ -0,0 +1,118 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.web.scripts.discussion; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import org.alfresco.query.PagingRequest; +import org.alfresco.query.PagingResults; +import org.alfresco.service.cmr.discussion.PostInfo; +import org.alfresco.service.cmr.discussion.TopicInfo; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.util.Pair; +import org.json.simple.JSONObject; +import org.springframework.extensions.webscripts.Cache; +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 discussions topics fetching forum-posts-hot.get webscript. + * + * @author Nick Burch + * @since 4.0 + */ +public class ForumTopicsHotGet extends AbstractDiscussionWebScript +{ + protected static final int RECENT_POSTS_PERIOD_DAYS = 30; + protected static final long ONE_DAY_MS = 24*60*60*1000; + + @Override + protected Map executeImpl(SiteInfo site, NodeRef nodeRef, + TopicInfo topic, PostInfo post, WebScriptRequest req, JSONObject json, + Status status, Cache cache) + { + // They shouldn't be trying to list of an existing Post or Topic + if (topic != null || post != null) + { + String error = "Can't list Topics inside an existing Topic or Post"; + throw new WebScriptException(Status.STATUS_BAD_REQUEST, error); + } + + // Grab the date range to search over + String numDaysS = req.getParameter("numdays"); + int numDays = RECENT_POSTS_PERIOD_DAYS; + if (numDaysS != null) + { + numDays = Integer.parseInt(numDaysS); + } + + Date now = new Date(); + Date since = new Date(now.getTime() - numDays*ONE_DAY_MS); + + // Get the topics with recent replies + PagingResults> topicsAndCounts = null; + PagingRequest paging = buildPagingRequest(req); + if (site != null) + { + topicsAndCounts = discussionService.listHotTopics(site.getShortName(), since, paging); + } + else + { + topicsAndCounts = discussionService.listHotTopics(nodeRef, since, paging); + } + + // For this, we actually only need the topics, not their counts too + List topics = new ArrayList(); + for (Pair tc : topicsAndCounts.getPage()) + { + topics.add(tc.getFirst()); + } + + + // If they did a site based search, and the component hasn't + // been created yet, use the site for the permissions checking + if (site != null && nodeRef == null) + { + nodeRef = site.getNodeRef(); + } + + + // Build the common model parts + Map model = buildCommonModel(site, topic, post, req); + model.put("forum", nodeRef); + + // Have the topics rendered + model.put("data", renderTopics(topics, topicsAndCounts.getTotalResultCount(), paging, site)); + + // All done + return model; + } +} diff --git a/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumTopicsMineGet.java b/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumTopicsMineGet.java new file mode 100644 index 0000000000..175b49042a --- /dev/null +++ b/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumTopicsMineGet.java @@ -0,0 +1,97 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.web.scripts.discussion; + +import java.util.Map; + +import org.alfresco.query.PagingRequest; +import org.alfresco.query.PagingResults; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.discussion.PostInfo; +import org.alfresco.service.cmr.discussion.TopicInfo; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.site.SiteInfo; +import org.json.simple.JSONObject; +import org.springframework.extensions.webscripts.Cache; +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 discussions topics fetching forum-posts-mine.get webscript. + * + * @author Nick Burch + * @since 4.0 + */ +public class ForumTopicsMineGet extends AbstractDiscussionWebScript +{ + @Override + protected Map executeImpl(SiteInfo site, NodeRef nodeRef, + TopicInfo topic, PostInfo post, WebScriptRequest req, JSONObject json, + Status status, Cache cache) + { + // They shouldn't be trying to list of an existing Post or Topic + if (topic != null || post != null) + { + String error = "Can't list Topics inside an existing Topic or Post"; + throw new WebScriptException(Status.STATUS_BAD_REQUEST, error); + } + + // Grab the current user to list for + String username = AuthenticationUtil.getFullyAuthenticatedUser(); + + // Get the topics for the user, oldest first + PagingResults topics = null; + PagingRequest paging = buildPagingRequest(req); + if (site != null) + { + topics = discussionService.listTopics(site.getShortName(), username, true, paging); + } + else + { + topics = discussionService.listTopics(nodeRef, username, true, paging); + } + + + // If they did a site based search, and the component hasn't + // been created yet, use the site for the permissions checking + if (site != null && nodeRef == null) + { + nodeRef = site.getNodeRef(); + } + + + // Build the common model parts + Map model = buildCommonModel(site, topic, post, req); + model.put("forum", nodeRef); + + // Have the topics rendered + model.put("data", renderTopics(topics, paging, site)); + + // All done + return model; + } +} diff --git a/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumTopicsRecentGet.java b/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumTopicsRecentGet.java new file mode 100644 index 0000000000..b9ea4a644d --- /dev/null +++ b/src/main/java/org/alfresco/repo/web/scripts/discussion/ForumTopicsRecentGet.java @@ -0,0 +1,109 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.web.scripts.discussion; + +import java.util.Date; +import java.util.Map; + +import org.alfresco.query.PagingRequest; +import org.alfresco.query.PagingResults; +import org.alfresco.service.cmr.discussion.PostInfo; +import org.alfresco.service.cmr.discussion.TopicInfo; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.site.SiteInfo; +import org.json.simple.JSONObject; +import org.springframework.extensions.webscripts.Cache; +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 discussions topics fetching forum-posts-new.get webscript. + * + * @author Nick Burch + * @since 4.0 + */ +public class ForumTopicsRecentGet extends AbstractDiscussionWebScript +{ + protected static final int RECENT_SEARCH_PERIOD_DAYS = 7; + protected static final long ONE_DAY_MS = 24*60*60*1000; + + @Override + protected Map executeImpl(SiteInfo site, NodeRef nodeRef, + TopicInfo topic, PostInfo post, WebScriptRequest req, JSONObject json, + Status status, Cache cache) + { + // They shouldn't be trying to list of an existing Post or Topic + if (topic != null || post != null) + { + String error = "Can't list Topics inside an existing Topic or Post"; + throw new WebScriptException(Status.STATUS_BAD_REQUEST, error); + } + + // Grab the date range to search over + String numDaysS = req.getParameter("numdays"); + int numDays = RECENT_SEARCH_PERIOD_DAYS; + if (numDaysS != null) + { + numDays = Integer.parseInt(numDaysS); + } + + Date now = new Date(); + Date from = new Date(now.getTime() - numDays*ONE_DAY_MS); + Date to = new Date(now.getTime() + ONE_DAY_MS); + + // Get the recent topics, newest first + PagingResults topics = null; + PagingRequest paging = buildPagingRequest(req); + if (site != null) + { + topics = discussionService.listTopics(site.getShortName(), from, to, false, paging); + } + else + { + topics = discussionService.listTopics(nodeRef, from, to, false, paging); + } + + + // If they did a site based search, and the component hasn't + // been created yet, use the site for the permissions checking + if (site != null && nodeRef == null) + { + nodeRef = site.getNodeRef(); + } + + + // Build the common model parts + Map model = buildCommonModel(site, topic, post, req); + model.put("forum", nodeRef); + + // Have the topics rendered + model.put("data", renderTopics(topics, paging, site)); + + // All done + return model; + } +} diff --git a/src/main/java/org/alfresco/rest/api/impl/FavouritesImpl.java b/src/main/java/org/alfresco/rest/api/impl/FavouritesImpl.java index 331bfc93ec..1c9e783318 100644 --- a/src/main/java/org/alfresco/rest/api/impl/FavouritesImpl.java +++ b/src/main/java/org/alfresco/rest/api/impl/FavouritesImpl.java @@ -26,6 +26,7 @@ package org.alfresco.rest.api.impl; import java.util.AbstractList; +import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -57,9 +58,11 @@ import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; import org.alfresco.rest.framework.resource.parameters.Paging; import org.alfresco.rest.framework.resource.parameters.Parameters; import org.alfresco.rest.framework.resource.parameters.Params; +import org.alfresco.rest.framework.resource.parameters.SortColumn; import org.alfresco.rest.framework.resource.parameters.where.QueryHelper; import org.alfresco.rest.framework.resource.parameters.where.QueryHelper.WalkerCallbackAdapter; import org.alfresco.service.cmr.favourites.FavouritesService; +import org.alfresco.service.cmr.favourites.FavouritesService.SortFields; import org.alfresco.service.cmr.favourites.FavouritesService.Type; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.StoreRef; @@ -307,7 +310,9 @@ public class FavouritesImpl implements Favourites personId = people.validatePerson(personId, true); Paging paging = parameters.getPaging(); - + + List> sortProps = getSortProps(parameters); + final Set filteredByClientQuery = new HashSet(); Set filterTypes = FavouritesService.Type.ALL_FILTER_TYPES; //Default all @@ -345,8 +350,8 @@ public class FavouritesImpl implements Favourites filterTypes = filteredByClientQuery; } - final PagingResults favourites = favouritesService.getPagedFavourites(personId, filterTypes, FavouritesService.DEFAULT_SORT_PROPS, - Util.getPagingRequest(paging)); + final PagingResults favourites = favouritesService.getPagedFavourites(personId, filterTypes, sortProps, Util.getPagingRequest(paging)); + return wrap(paging, favourites, parameters); } @@ -370,4 +375,32 @@ public class FavouritesImpl implements Favourites Parameters parameters = Params.valueOf(recognizedParams, personId, favouriteId, null); return parameters; } + + private List> getSortProps(Parameters parameters) + { + List> sortProps = new ArrayList<>(); + List sortCols = parameters.getSorting(); + if ((sortCols != null) && (sortCols.size() > 0)) + { + for (SortColumn sortCol : sortCols) + { + SortFields sortField; + try + { + sortField = SortFields.valueOf(sortCol.column); + } + catch (Exception e) + { + throw new InvalidArgumentException("Invalid sort field: " + sortCol.column); + } + sortProps.add(new Pair<>(sortField, (sortCol.asc ? Boolean.TRUE : Boolean.FALSE))); + } + } + else + { + // default sort order + sortProps = FavouritesService.DEFAULT_SORT_PROPS; + } + return sortProps; + } } diff --git a/src/main/resources/alfresco/public-rest-context.xml b/src/main/resources/alfresco/public-rest-context.xml index b85ef4e7df..a99d06aa86 100644 --- a/src/main/resources/alfresco/public-rest-context.xml +++ b/src/main/resources/alfresco/public-rest-context.xml @@ -327,7 +327,16 @@ - + + + + + + org.alfresco.discussions.reply-created + + + + diff --git a/src/main/resources/alfresco/templates/activities/org/alfresco/discussions/post-created.atomentry.ftl b/src/main/resources/alfresco/templates/activities/org/alfresco/discussions/post-created.atomentry.ftl new file mode 100644 index 0000000000..b6c4583005 --- /dev/null +++ b/src/main/resources/alfresco/templates/activities/org/alfresco/discussions/post-created.atomentry.ftl @@ -0,0 +1,14 @@ +<#include "../slingshot-common.lib.ftl"> + + New discussion: ${htmlTitle?xml} + + http://www.alfresco.org/rss/atom/${id} + ${xmldate(date)} + + + + + ${userName?xml} + ${userId?xml} + + \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/activities/org/alfresco/discussions/post-deleted.atomentry.ftl b/src/main/resources/alfresco/templates/activities/org/alfresco/discussions/post-deleted.atomentry.ftl new file mode 100644 index 0000000000..013510cacb --- /dev/null +++ b/src/main/resources/alfresco/templates/activities/org/alfresco/discussions/post-deleted.atomentry.ftl @@ -0,0 +1,14 @@ +<#include "../slingshot-common.lib.ftl"> + + Discussion deleted: ${htmlTitle?xml} + + http://www.alfresco.org/rss/atom/${id} + ${xmldate(date)} + + + + + ${userName?xml} + ${userId?xml} + + \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/activities/org/alfresco/discussions/post-updated.atomentry.ftl b/src/main/resources/alfresco/templates/activities/org/alfresco/discussions/post-updated.atomentry.ftl new file mode 100644 index 0000000000..097dab3bdd --- /dev/null +++ b/src/main/resources/alfresco/templates/activities/org/alfresco/discussions/post-updated.atomentry.ftl @@ -0,0 +1,14 @@ +<#include "../slingshot-common.lib.ftl"> + + Discussion updated: ${htmlTitle?xml} + + http://www.alfresco.org/rss/atom/${id} + ${xmldate(date)} + + + + + ${userName?xml} + ${userId?xml} + + \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/activities/org/alfresco/discussions/reply-created.atomentry.ftl b/src/main/resources/alfresco/templates/activities/org/alfresco/discussions/reply-created.atomentry.ftl new file mode 100644 index 0000000000..2c062fcdb5 --- /dev/null +++ b/src/main/resources/alfresco/templates/activities/org/alfresco/discussions/reply-created.atomentry.ftl @@ -0,0 +1,14 @@ +<#include "../slingshot-common.lib.ftl"> + + Reply added to ${(htmlTitle!'')?html?xml} + + http://www.alfresco.org/rss/atom/${id} + ${xmldate(date)} + + + + + ${userName?xml} + ${userId?xml} + + \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/activities/org/alfresco/discussions/reply-updated.atomentry.ftl b/src/main/resources/alfresco/templates/activities/org/alfresco/discussions/reply-updated.atomentry.ftl new file mode 100644 index 0000000000..25d4c3503c --- /dev/null +++ b/src/main/resources/alfresco/templates/activities/org/alfresco/discussions/reply-updated.atomentry.ftl @@ -0,0 +1,14 @@ +<#include "../slingshot-common.lib.ftl"> + + Reply to ${(htmlTitle!'')?html?xml} updated + + http://www.alfresco.org/rss/atom/${id} + ${xmldate(date)} + + + + + ${userName?xml} + ${userId?xml} + + \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/RetrieveEventDefaults.get.desc.xml b/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/RetrieveEventDefaults.get.desc.xml deleted file mode 100644 index c671e4a62b..0000000000 --- a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/RetrieveEventDefaults.get.desc.xml +++ /dev/null @@ -1,9 +0,0 @@ - - Retrieve Event Defaults - Retrieve Event Defaults - /calendar/RetrieveEventDefaults?s={spaceRef} - extension - user - required - internal - \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/RetrieveEventDefaults.get.html.ftl b/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/RetrieveEventDefaults.get.html.ftl deleted file mode 100644 index 967faeb995..0000000000 --- a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/RetrieveEventDefaults.get.html.ftl +++ /dev/null @@ -1 +0,0 @@ -${result} \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/RetrieveEventDefaults.get.js b/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/RetrieveEventDefaults.get.js deleted file mode 100644 index cf98f3b678..0000000000 --- a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/RetrieveEventDefaults.get.js +++ /dev/null @@ -1,54 +0,0 @@ -function getGUIDFromNodeRef(nodeRef) -{ - var str = "" + nodeRef; - return str.substring(str.lastIndexOf("/")+1); -} - -function findNodeByNodeRef(nodeRef) -{ - var resultsArray= search.luceneSearch("ID:workspace\\://SpacesStore/" + getGUIDFromNodeRef(nodeRef)); - - if (resultsArray != null && resultsArray.length > 0) - { - return resultsArray[0]; - } - else - { - return null; - } -} - -var spaceRef = args.s; -var currentBaseSpace = findNodeByNodeRef(spaceRef); -var response = "" -var _today = new Date(); - -if (currentBaseSpace != null) -{ - if (currentBaseSpace.properties["ia:whatEventDefault"] != null) - response += currentBaseSpace.properties["ia:whatEventDefault"] + "^"; - else - response += "" + "^"; - - if (currentBaseSpace.properties["ia:fromDateDefault"] != null) - response += currentBaseSpace.properties["ia:fromDateDefault"] + "^"; - else - response += _today.toString() + "^"; - - if (currentBaseSpace.properties["ia:toDateDefault"] != null) - response += currentBaseSpace.properties["ia:toDateDefault"] + "^"; - else - response += _today.toString() + "^"; - - if (currentBaseSpace.properties["ia:whereEventDefault"] != null) - response += currentBaseSpace.properties["ia:whereEventDefault"] + "^"; - else - response += "" + "^"; - - if (currentBaseSpace.properties["ia:colorEventDefault"] != null) - response += currentBaseSpace.properties["ia:colorEventDefault"]; - else - response += ""; -} -logger.log("RESPONSE: " + response); -model.result = response; diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/RetrieveEventDetails.get.desc.xml b/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/RetrieveEventDetails.get.desc.xml deleted file mode 100644 index aee285c527..0000000000 --- a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/RetrieveEventDetails.get.desc.xml +++ /dev/null @@ -1,9 +0,0 @@ - - Retrieve Event Details - Retrieve Event Details - /calendar/RetrieveEventDetails?e={eventId} - extension - user - required - internal - \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/RetrieveEventDetails.get.html.ftl b/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/RetrieveEventDetails.get.html.ftl deleted file mode 100644 index 967faeb995..0000000000 --- a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/RetrieveEventDetails.get.html.ftl +++ /dev/null @@ -1 +0,0 @@ -${result} \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/RetrieveEventDetails.get.js b/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/RetrieveEventDetails.get.js deleted file mode 100644 index 1215dae1f9..0000000000 --- a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/RetrieveEventDetails.get.js +++ /dev/null @@ -1,35 +0,0 @@ -var eventId = args.e; -var eventNode = search.findNode(eventId); - -if (eventNode == null) -{ - model.result = "Event not Found"; -} - -else -{ - var response = ""; - var _fromDate = eventNode.properties["ia:fromDate"]; - var _toDate = eventNode.properties["ia:toDate"]; - var _fromMonth = _fromDate.getMonth() + 1; - var _toMonth = _toDate.getMonth() + 1; - - response += eventNode.properties["ia:whatEvent"] + "^"; - response += _fromMonth + "/" + _fromDate.getDate() + "/" + _fromDate.getFullYear() + "^"; - var frmHour = _fromDate.getHours(); - if (frmHour == 0) frmHour += "0"; - var frmMin = _fromDate.getMinutes(); - if (frmMin == 0) frmMin += "0"; - response += frmHour + ":" + frmMin + "^"; - response += _toMonth + "/" + _toDate.getDate() + "/" + _toDate.getFullYear() + "^"; - var toHour = _toDate.getHours(); - if (toHour == 0) toHour += "0"; - var toMin = _toDate.getMinutes(); - if (toMin == 0) toMin += "0"; - response += toHour + ":" + toMin + "^"; - response += eventNode.properties["ia:whereEvent"] + "^"; - response += eventNode.properties["ia:descriptionEvent"] + "^"; - response += eventNode.properties["ia:colorEvent"]; - - model.result=response; -} diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/SaveCalendarEvent.get.desc.xml b/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/SaveCalendarEvent.get.desc.xml deleted file mode 100644 index b5fa26d13d..0000000000 --- a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/SaveCalendarEvent.get.desc.xml +++ /dev/null @@ -1,9 +0,0 @@ - - Save Calendar Event - Save Calendar Event - /calendar/SaveCalendarEvent?what={whatEvent}&where={whereEvent}&desc={descriptionEvent}&color={colorEvent}&fd={fromDate}&ft={fromTime}&td={toDate}&tt={toTime}&e={eventId}&d={toDelete}&s={spaceRef} - extension - user - required - internal - \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/SaveCalendarEvent.get.html.ftl b/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/SaveCalendarEvent.get.html.ftl deleted file mode 100644 index 967faeb995..0000000000 --- a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/SaveCalendarEvent.get.html.ftl +++ /dev/null @@ -1 +0,0 @@ -${result} \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/SaveCalendarEvent.get.js b/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/SaveCalendarEvent.get.js deleted file mode 100644 index 82df07e82d..0000000000 --- a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/SaveCalendarEvent.get.js +++ /dev/null @@ -1,103 +0,0 @@ -var now = new Date(); -var day = now.getDay(); -var month = now.getMonth(); -var year = now.getYear(); -var hour = now.getHours(); -var minute = now.getMinutes(); -var second = now.getSeconds(); - -var stamp = day + month + year + hour.toString() + minute + second; -logger.log("DATE: " + args.fd + " " + args.ft); -var fromDateString = args.fd + " " + args.ft; -var fromDateDate = new Date(fromDateString); - -var toDateString = args.td + " " + args.tt; -var toDateDate = new Date(toDateString); - -var content = "What: " + args.what + "
"; -content += "When: " + args.fd + " " + args.ft + " to " + args.td + " " + args.tt + "
"; -content += "Where: " + args.where + "
"; -content += "Description: " + args.desc + "
"; -content += "Color: " + args.color; - -var fileNode = null; -var eventId = args.e; -var toDelete = args.d; - -var spaceRef = args.s; -var nodeWhereToCreate = findNodeByNodeRef(spaceRef); - -var response; - -if (nodeWhereToCreate != null) -{ - var newFolder = nodeWhereToCreate.childByNamePath("CalEvents"); - if (newFolder == null) - nodeWhereToCreate = nodeWhereToCreate.createFolder("CalEvents"); - else - nodeWhereToCreate = newFolder; - - if (eventId == null) - { - fileNode = nodeWhereToCreate.createNode(stamp + ".ics", "ia:calendarEvent"); - saveNodeDetails(); - } - else - { - fileNode = search.findNode(eventId); - if (toDelete == 'true') - { - var status = fileNode.remove(); - if (status) - response = "DELETED"; - else - response = "NOT DELETED"; - } - else - { - saveNodeDetails(); - } - } -} -else -{ - response = "SPACE not found"; -} - -model.result = response; - - -function saveNodeDetails() -{ - fileNode.properties["ia:whatEvent"] = args.what; - fileNode.properties["ia:fromDate"] = fromDateDate; - fileNode.properties["ia:toDate"] = toDateDate; - fileNode.properties["ia:whereEvent"] = args.where; - fileNode.properties["ia:descriptionEvent"] = args.desc; - fileNode.properties["ia:colorEvent"] = args.color; - - fileNode.save(); - fileNode.content = content; - - response = fileNode.content; -} - -function getGUIDFromNodeRef(nodeRef) -{ - var str = "" + nodeRef; - return str.substring(str.lastIndexOf("/")+1); -} - -function findNodeByNodeRef(nodeRef) -{ - var resultsArray = search.luceneSearch("ID:workspace\\://SpacesStore/" + getGUIDFromNodeRef(nodeRef)); - - if (resultsArray != null && resultsArray.length > 0) - { - return resultsArray[0]; - } - else - { - return null; - } -} diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarDelete.post.desc.xml b/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarDelete.post.desc.xml deleted file mode 100644 index 8e5ba6ef25..0000000000 --- a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarDelete.post.desc.xml +++ /dev/null @@ -1,7 +0,0 @@ - - Calendar Subscriptions - Calendar Subscriptions - /calendar/calendarRemove - user - internal - \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarDelete.post.html.ftl b/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarDelete.post.html.ftl deleted file mode 100644 index 27c7e90446..0000000000 --- a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarDelete.post.html.ftl +++ /dev/null @@ -1,6 +0,0 @@ - - \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarDelete.post.js b/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarDelete.post.js deleted file mode 100644 index 88401a3577..0000000000 --- a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarDelete.post.js +++ /dev/null @@ -1,33 +0,0 @@ -function getGUIDFromNodeRef(nodeRef) { - var str = "" + nodeRef; - if (str.length > 24) { - return str.substring(24); - } else { - return ""; - } -} - -function findNodeByNodeRef(nodeRef) { - var resultsArray = search.luceneSearch("ID:workspace\\://SpacesStore/" + getGUIDFromNodeRef(nodeRef)); - if (resultsArray != null && resultsArray.length > 0) { - return resultsArray[0]; - } else { - return null; - } -} - -var spaceRef = args.ref; -var space = findNodeByNodeRef(spaceRef); - -// Resolve the calendar reference -var calendar = findNodeByNodeRef(args.calendar); -if (calendar != null) { - space.removeAssociation(calendar, "ia:subscribedCalendarList"); - space.save(); -} - -var calendars = space.assocs["ia:subscribedCalendarList"]; -if (calendars == null) { - calendars = new Array(); -} -model.resultset = calendars; diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarEvents.get.desc.xml b/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarEvents.get.desc.xml deleted file mode 100644 index be13bd3cff..0000000000 --- a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarEvents.get.desc.xml +++ /dev/null @@ -1,8 +0,0 @@ - - Calendar Events - Calendar Events - /calendar/getCalendarEvents - user - required - internal - \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarEvents.get.html.ftl b/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarEvents.get.html.ftl deleted file mode 100644 index efb408c9b4..0000000000 --- a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarEvents.get.html.ftl +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - -
diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarEvents.get.js b/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarEvents.get.js deleted file mode 100644 index faf4165081..0000000000 --- a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarEvents.get.js +++ /dev/null @@ -1,87 +0,0 @@ -function getGUIDFromNodeRef(nodeRef) { - var str = "" + nodeRef; - if (str.length > 24) { - return str.substring(24); - } else { - return ""; - } -} - -function findNodeByNodeRef(nodeRef) { - var resultsArray = search.luceneSearch("ID:workspace\\://SpacesStore/" + getGUIDFromNodeRef(nodeRef)); - if (resultsArray != null && resultsArray.length > 0) { - return resultsArray[0]; - } else { - return null; - } -} - -var spaceRef = args.ref; -var calendar = findNodeByNodeRef(spaceRef); - -function formatANSI(datestr) { - var tmp = datestr.split("/"); - var d = new Date(); - d.setFullYear(tmp[0]); - d.setMonth(tmp[1]-1); - d.setDate(tmp[2]); - return d; -} - -function Interval(start, end) { - this.start = start; - this.end = end; -}; - -Interval.prototype.overlaps = function(interval) { - // take the interval with the early start time as the one to compare against - var x, y; - if (this.start.getTime() < interval.start.getTime()) { - x = this; y = interval; - } else { - x = interval; y = this; - } - var time = y.start.getTime(); - return (x.start.getTime() <= time) && (time < this.end.getTime()); -}; - -function isLeap(year) { - return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)); -} - -var monthToDays = [31,28,31,30,31,30,31,31,30,31,30,31]; - -if (isLeap(args.year)) { - monthToDays[1] = 29; // one extra day in February -} - -var fromDate = args.year + "/0" + args.month + "/01"; -var from = formatANSI(fromDate); -var toDate = args.year + "/0" + args.month + "/" + monthToDays[args.month - 1]; -var to = formatANSI(toDate); -var time = new Interval(from, to); - -var selectedDates = new Array(); -var events = calendar.children; - -for(var i=0; i < events.length; i++) { - var event = events[i]; - var startdate = new Date(event.properties["ia:fromDate"]); - var enddate = new Date(event.properties["ia:toDate"]); - var interval = new Interval(startdate, enddate); - if (interval.overlaps(time)) { - var key = (interval.start.getMonth()+1) + "/" + interval.start.getDate(); - if (selectedDates.indexOf(key) < 0) { - selectedDates.push(key); - } - } -} - -model.dates = selectedDates; - -// These are only used for the html view -model.month = args.month; -model.year = args.year; - - - diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarEvents.get.json.ftl b/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarEvents.get.json.ftl deleted file mode 100644 index a154d98d27..0000000000 --- a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarEvents.get.json.ftl +++ /dev/null @@ -1,3 +0,0 @@ -{ -results: [<#list dates as d>"${d}"<#if d_has_next>,] -} \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarInit.get.desc.xml b/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarInit.get.desc.xml deleted file mode 100644 index a0ee3247ff..0000000000 --- a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarInit.get.desc.xml +++ /dev/null @@ -1,7 +0,0 @@ - - Calendar Subscriptions - Calendar Subscriptions - /calendar/calendarInit - user - internal - \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarInit.get.html.ftl b/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarInit.get.html.ftl deleted file mode 100644 index 0d9925de4e..0000000000 --- a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarInit.get.html.ftl +++ /dev/null @@ -1,54 +0,0 @@ -
- - - - - -
-
 
-
-Select a color -
-
-<#-- This MUST be present --> -
- - - - - - - - - - - - - - - -
Available Calendars Subscribed Calendars
- - - - -
-<#-- Display the list of available calendars--> - -
-
- - - -<#-- Display the list of subscribed calendars--> - -
 
- \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarInit.get.js b/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarInit.get.js deleted file mode 100644 index 6471dc0ec3..0000000000 --- a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarInit.get.js +++ /dev/null @@ -1,53 +0,0 @@ -function getGUIDFromNodeRef(nodeRef) { - var str = "" + nodeRef; - if (str.length > 24) { - return str.substring(24); - } else { - return ""; - } -} - -function findNodeByNodeRef(nodeRef) { - var resultsArray = search.luceneSearch("ID:workspace\\://SpacesStore/" + getGUIDFromNodeRef(nodeRef)); - if (resultsArray != null && resultsArray.length > 0) { - return resultsArray[0]; - } else { - return null; - } -} - -var spaceRef = args.ref; -var calendar = findNodeByNodeRef(spaceRef); - -var color = calendar.properties["ia:colorEventDefault"]; -if (color == null || color.length == 0) { - color = "#FF0000"; // default -} -model.color = color; - -// Get the list of calendars the user is subscribed to -var subscriptions = calendar.assocs["ia:subscribedCalendarList"] -if (subscriptions == null) { - subscriptions = new Array(); -} -model.subscriptions = subscriptions; - -// perform search -var nodes = search.luceneSearch('TYPE:\"{http\://www.alfresco.org/model/calendar}calendar\"'); - -/** -var filtered = new Array(); - if (nodes.length > 0) { - var re = new RegExp(args.q,"i"); - var j = 0; - for(i=0; i < nodes.length; i++) { - var n = nodes[i]; - if (re.test(n.parent.name)) { - filtered[j] = n; - ++j; - } - } - } -**/ - -model.available = nodes; \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarSub.post.desc.xml b/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarSub.post.desc.xml deleted file mode 100644 index 49bcd5b765..0000000000 --- a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarSub.post.desc.xml +++ /dev/null @@ -1,7 +0,0 @@ - - Calendar Subscriptions - Calendar Subscriptions - /calendar/calendarSubscriptions - user - internal - \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarSub.post.html.ftl b/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarSub.post.html.ftl deleted file mode 100644 index 27c7e90446..0000000000 --- a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarSub.post.html.ftl +++ /dev/null @@ -1,6 +0,0 @@ - - \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarSub.post.js b/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarSub.post.js deleted file mode 100644 index 9633572aa5..0000000000 --- a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/calendarSub.post.js +++ /dev/null @@ -1,29 +0,0 @@ -function getGUIDFromNodeRef(nodeRef) { - var str = "" + nodeRef; - if (str.length > 24) { - return str.substring(24); - } else { - return ""; - } -} - -function findNodeByNodeRef(nodeRef) { - var resultsArray = search.luceneSearch("ID:workspace\\://SpacesStore/" + getGUIDFromNodeRef(nodeRef)); - if (resultsArray != null && resultsArray.length > 0) { - return resultsArray[0]; - } else { - return null; - } -} - -var spaceRef = args.ref; -var space = findNodeByNodeRef(spaceRef); -// Resolve the calendar reference -var calendar = findNodeByNodeRef(args.calendar); -if (calendar != null) { - space.createAssociation(calendar, "ia:subscribedCalendarList"); - space.save(); -} - -var calendars = space.assocs["ia:subscribedCalendarList"]; -model.resultset = calendars; diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/color.get.desc.xml b/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/color.get.desc.xml deleted file mode 100644 index a9a044f955..0000000000 --- a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/color.get.desc.xml +++ /dev/null @@ -1,8 +0,0 @@ - - Calendar Subscriptions - Calendar Subscriptions - /calendar/getColor - user - required - internal - \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/color.get.html.ftl b/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/color.get.html.ftl deleted file mode 100644 index 58e0854d3b..0000000000 --- a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/color.get.html.ftl +++ /dev/null @@ -1,3 +0,0 @@ -${color} - - \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/color.get.js b/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/color.get.js deleted file mode 100644 index 8348a686fe..0000000000 --- a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/color.get.js +++ /dev/null @@ -1,31 +0,0 @@ -function getGUIDFromNodeRef(nodeRef) { - var str = "" + nodeRef; - if (str.length > 24) { - return str.substring(24); - } else { - return ""; - } -} - -function findNodeByNodeRef(nodeRef) { - var resultsArray = search.luceneSearch("ID:workspace\\://SpacesStore/" + getGUIDFromNodeRef(nodeRef)); - if (resultsArray != null && resultsArray.length > 0) { - return resultsArray[0]; - } else { - return null; - } -} - -var spaceRef = args.ref; -var color = null; -var calendar = findNodeByNodeRef(spaceRef); - -if (calendar != null) { - color = calendar.properties["ia:colorEventDefault"]; -} - -if (color == null || color.length == 0) { - color = "#FF0000"; // red -} - -model.color = color; diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/color.post.desc.xml b/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/color.post.desc.xml deleted file mode 100644 index a1cb61f8a7..0000000000 --- a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/color.post.desc.xml +++ /dev/null @@ -1,8 +0,0 @@ - - Calendar Subscriptions - Calendar Subscriptions - /calendar/setColor - user - required - internal - \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/color.post.html.ftl b/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/color.post.html.ftl deleted file mode 100644 index 7014099827..0000000000 --- a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/color.post.html.ftl +++ /dev/null @@ -1,2 +0,0 @@ -Done. - \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/color.post.js b/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/color.post.js deleted file mode 100644 index 0f01b724b6..0000000000 --- a/src/main/resources/alfresco/templates/webscripts/org/alfresco/calendar/color.post.js +++ /dev/null @@ -1,27 +0,0 @@ -function getGUIDFromNodeRef(nodeRef) { - var str = "" + nodeRef; - if (str.length > 24) { - return str.substring(24); - } else { - return ""; - } -} - -function findNodeByNodeRef(nodeRef) { - var resultsArray = search.luceneSearch("ID:workspace\\://SpacesStore/" + getGUIDFromNodeRef(nodeRef)); - if (resultsArray != null && resultsArray.length > 0) { - return resultsArray[0]; - } else { - return null; - } -} -logger.log("COLOR: " + args.color + " REF: " + args.ref); -var spaceRef = args.ref; -var calendar = findNodeByNodeRef(spaceRef); - -if (calendar != null) { - calendar.properties["ia:colorEventDefault"] = args.color; - calendar.save(); -} - - diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts-filtered.get.desc.xml b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts-filtered.get.desc.xml new file mode 100644 index 0000000000..ea7bd1ff06 --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts-filtered.get.desc.xml @@ -0,0 +1,10 @@ + + Get filtered forum posts + Get the discussion topics that match the filters. + /api/forum/site/{site}/discussions/posts/filtered + /api/forum/discussions/posts/filtered + argument + user + required + limited_support + diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts-filtered.get.json.ftl b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts-filtered.get.json.ftl new file mode 100644 index 0000000000..f338b40df2 --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts-filtered.get.json.ftl @@ -0,0 +1,19 @@ +<#import "../post.lib.ftl" as postLib/> +<#import "../../generic-paged-results.lib.ftl" as gen/> +{ + "forumPermissions": + { + <#if forum??> + "create": ${forum.hasPermission("CreateChildren")?string}, + "edit": ${forum.hasPermission("Write")?string}, + "delete": ${forum.hasPermission("Delete")?string} + <#else> + "create": false, + "edit": false, + "delete": false + + }, +<@gen.pagedResults data=data ; item> + <@postLib.postJSON postData=item /> + +} \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts-hot.get.desc.xml b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts-hot.get.desc.xml new file mode 100644 index 0000000000..910f5fe038 --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts-hot.get.desc.xml @@ -0,0 +1,11 @@ + + Get hot forum posts + Get the hot topics in the forum. + /api/forum/site/{site}/{container}/{path}/posts/hot + /api/forum/site/{site}/{container}/posts/hot + /api/forum/node/{store_type}/{store_id}/{id}/posts/hot + argument + user + required + limited_support + diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts-hot.get.json.ftl b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts-hot.get.json.ftl new file mode 100644 index 0000000000..d60a684a3e --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts-hot.get.json.ftl @@ -0,0 +1,13 @@ +<#import "../post.lib.ftl" as postLib/> +<#import "../../generic-paged-results.lib.ftl" as gen/> +{ + "forumPermissions": + { + "create": ${forum.hasPermission("CreateChildren")?string}, + "edit": ${forum.hasPermission("Write")?string}, + "delete": ${forum.hasPermission("Delete")?string} + }, +<@gen.pagedResults data=data ; item> + <@postLib.postJSON postData=item /> + +} diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts-mine.get.desc.xml b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts-mine.get.desc.xml new file mode 100644 index 0000000000..ef849922f0 --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts-mine.get.desc.xml @@ -0,0 +1,11 @@ + + Get my forum posts + Gets the forum posts created by the current user. + /api/forum/site/{site}/{container}/{path}/posts/myposts + /api/forum/site/{site}/{container}/posts/myposts + /api/forum/node/{store_type}/{store_id}/{id}/posts/myposts + argument + user + required + limited_support + diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts-mine.get.json.ftl b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts-mine.get.json.ftl new file mode 100644 index 0000000000..d60a684a3e --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts-mine.get.json.ftl @@ -0,0 +1,13 @@ +<#import "../post.lib.ftl" as postLib/> +<#import "../../generic-paged-results.lib.ftl" as gen/> +{ + "forumPermissions": + { + "create": ${forum.hasPermission("CreateChildren")?string}, + "edit": ${forum.hasPermission("Write")?string}, + "delete": ${forum.hasPermission("Delete")?string} + }, +<@gen.pagedResults data=data ; item> + <@postLib.postJSON postData=item /> + +} diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts-new.get.desc.xml b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts-new.get.desc.xml new file mode 100644 index 0000000000..629218eb72 --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts-new.get.desc.xml @@ -0,0 +1,11 @@ + + Get new forum posts + Gets the forum posts for the last specified number of days. + /api/forum/site/{site}/{container}/{path}/posts/new?numdays={numdays} + /api/forum/site/{site}/{container}/posts/new?numdays={numdays} + /api/forum/node/{store_type}/{store_id}/{id}/posts/new?numdays={numdays} + argument + user + required + limited_support + diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts-new.get.json.ftl b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts-new.get.json.ftl new file mode 100644 index 0000000000..d60a684a3e --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts-new.get.json.ftl @@ -0,0 +1,13 @@ +<#import "../post.lib.ftl" as postLib/> +<#import "../../generic-paged-results.lib.ftl" as gen/> +{ + "forumPermissions": + { + "create": ${forum.hasPermission("CreateChildren")?string}, + "edit": ${forum.hasPermission("Write")?string}, + "delete": ${forum.hasPermission("Delete")?string} + }, +<@gen.pagedResults data=data ; item> + <@postLib.postJSON postData=item /> + +} diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts.get.desc.xml b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts.get.desc.xml new file mode 100644 index 0000000000..272aa32e82 --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts.get.desc.xml @@ -0,0 +1,11 @@ + + Get forum posts + Gets the forum posts. + /api/forum/site/{site}/{container}/{path}/posts + /api/forum/site/{site}/{container}/posts + /api/forum/node/{store_type}/{store_id}/{id}/posts + argument + user + required + limited_support + \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts.get.json.ftl b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts.get.json.ftl new file mode 100644 index 0000000000..6ba3e8ae02 --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts.get.json.ftl @@ -0,0 +1,19 @@ +<#import "../post.lib.ftl" as postLib/> +<#import "../../generic-paged-results.lib.ftl" as gen/> +{ + "forumPermissions": + { + <#if forum.getParent()?? && forum.getTypeShort() != "st:site" > + "create": ${(forum.getParent()).hasPermission("CreateChildren")?string}, + "edit": ${(forum.getParent()).hasPermission("Write")?string}, + "delete": ${(forum.getParent()).hasPermission("Delete")?string} + <#else> + "create": ${forum.hasPermission("CreateChildren")?string}, + "edit": ${forum.hasPermission("Write")?string}, + "delete": ${forum.hasPermission("Delete")?string} + + }, +<@gen.pagedResults data=data ; item> + <@postLib.postJSON postData=item /> + +} diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts.post.desc.xml b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts.post.desc.xml new file mode 100644 index 0000000000..94aed0bf27 --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts.post.desc.xml @@ -0,0 +1,11 @@ + + Add forum post + Adds a post to a forum. + /api/forum/site/{site}/{container}/{path}/posts + /api/forum/site/{site}/{container}/posts + /api/forum/node/{store_type}/{store_id}/{id}/posts + argument + user + required + limited_support + \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts.post.json.ftl b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts.post.json.ftl new file mode 100644 index 0000000000..a3cd1be869 --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts.post.json.ftl @@ -0,0 +1,4 @@ +<#import "../post.lib.ftl" as postLib /> +{ + "item": <@postLib.postJSON postData=postData /> +} \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/post.lib.ftl b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/post.lib.ftl new file mode 100644 index 0000000000..b48ca0a66d --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/post.lib.ftl @@ -0,0 +1,124 @@ +<#-- Renders a person object. --> +<#macro renderPerson person fieldName> +<#escape x as jsonUtils.encodeJSONString(x)> + "${fieldName}": + { + <#if person?has_content> + <#if person.assocs["cm:avatar"]??> + "avatarRef": "${person.assocs["cm:avatar"][0].nodeRef?string}", + + "username": "${person.properties["cm:userName"]}", + "firstName": "${person.properties["cm:firstName"]!""}", + "lastName": "${person.properties["cm:lastName"]!""}" + + }, + + + + +<#macro addContent post> +<#escape x as jsonUtils.encodeJSONString(x)> + <#assign safecontent=stringUtils.stripUnsafeHTML(post.content)> + <#if (contentLength?? && contentLength > -1 && (safecontent?length > contentLength))> + "content": "${safecontent?substring(0, contentLength)}", + <#else> + "content": "${safecontent}", + + + + + +<#macro postJSON postData> +{ + <@postDataJSON postData=postData /> +} + + +<#macro postDataJSON postData> +<#escape x as jsonUtils.encodeJSONString(x)> + <#-- which node should be used for urls? which for the post data? --> + <#if postData.isTopicPost> + <#assign refNode=postData.topic /> + <#else> + <#assign refNode=postData.post /> + + + <#assign post=postData.post /> + + <#-- render topic post only data first --> + <#if postData.isTopicPost> + "name": "${postData.topic.name}", + "totalReplyCount": ${postData.totalReplyCount?c}, + <#if postData.lastReply??> + "lastReplyOn": "${xmldate(postData.lastReply.properties.created)}", + <#if postData.lastReplyBy??> + <@renderPerson person=postData.lastReplyBy fieldName="lastReplyBy" /> + <#else> + "lastReplyBy": + { + "username": "" + }, + + + "tags": [<#list postData.tags as x>"${x}"<#if x_has_next>, ], + "site": "${postData.site!""}", + <#else> + "name": "${post.name}", + + + <#-- data using refNode which might be the topic or the post node --> + "url": "/forum/post/node/${refNode.nodeRef.storeRef.protocol}/${refNode.nodeRef.storeRef.identifier}/${refNode.nodeRef.id}", + "repliesUrl": "/forum/post/node/${refNode.nodeRef.storeRef.protocol}/${refNode.nodeRef.storeRef.identifier}/${refNode.nodeRef.id}/replies", + "nodeRef": "${refNode.nodeRef}", + + <#-- data coming from the post node --> + "title": "${(post.properties.title!"")}", + "createdOn": "${xmldate(post.properties.created)}", + "modifiedOn": "${xmldate(post.properties.modified)}", + <#if (post.properties["cm:updated"]??)> + "isUpdated": true, + "updatedOn": "${xmldate(post.properties["cm:updated"])}", + <#else> + "isUpdated": false, + + <#if postData.author?? && postData.author?has_content> + <@renderPerson person=postData.author fieldName="author" /> + <#else> + "author": + { + "username": "${post.properties["cm:creator"]}" + }, + + <@addContent post=post /> + "replyCount": <#if post.sourceAssocs["cm:references"]??>${post.sourceAssocs["cm:references"]?size?c}<#else>0, + "permissions": + { + "edit": ${postData.canEdit?string}, + "reply": ${post.parent.hasPermission("CreateChildren")?string}, + "delete": ${post.hasPermission("Delete")?string} + } + + + + +<#-- Renders replies. + The difference is to a normal post is that the children might be + added inline in the returned JSON. +--> +<#macro repliesJSON data> +{ + <@postDataJSON postData=data /> + <#if data.children?exists> + , "children": <@repliesRootJSON children=data.children /> + +} + + +<#macro repliesRootJSON children> +[ + <#list children as child> + <@repliesJSON data=child/> + <#if child_has_next>, + +] + diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post-replies.get.desc.xml b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post-replies.get.desc.xml new file mode 100644 index 0000000000..29b9fbdbce --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post-replies.get.desc.xml @@ -0,0 +1,10 @@ + + Get forum post replies + Get the forum post replies. + /api/forum/post/site/{site}/{container}/{path}/replies + /api/forum/post/node/{store_type}/{store_id}/{id}/replies + argument + user + required + limited_support + diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post-replies.get.json.ftl b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post-replies.get.json.ftl new file mode 100644 index 0000000000..2c4163b0d7 --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post-replies.get.json.ftl @@ -0,0 +1,4 @@ +<#import "../post.lib.ftl" as postLib /> +{ + "items": <@postLib.repliesRootJSON children=data /> +} diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post-replies.post.desc.xml b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post-replies.post.desc.xml new file mode 100644 index 0000000000..68fabdd713 --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post-replies.post.desc.xml @@ -0,0 +1,9 @@ + + Add forum post reply + Adds a reply to a post. + /api/forum/post/node/{store_type}/{store_id}/{id}/replies + argument + user + required + limited_support + \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post-replies.post.json.ftl b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post-replies.post.json.ftl new file mode 100644 index 0000000000..a3cd1be869 --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post-replies.post.json.ftl @@ -0,0 +1,4 @@ +<#import "../post.lib.ftl" as postLib /> +{ + "item": <@postLib.postJSON postData=postData /> +} \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete.desc.xml b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete.desc.xml new file mode 100644 index 0000000000..b179fc4052 --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete.desc.xml @@ -0,0 +1,10 @@ + + Delete topic + Deletes a topic. + /api/forum/post/site/{site}/{container}/{path} + /api/forum/post/node/{store_type}/{store_id}/{id} + argument + user + required + limited_support + \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete.json.ftl b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete.json.ftl new file mode 100644 index 0000000000..9a22868515 --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete.json.ftl @@ -0,0 +1,3 @@ +{ + "message" : "${message}" +} \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete.properties b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete.properties new file mode 100644 index 0000000000..062cb99eb9 --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete.properties @@ -0,0 +1,2 @@ +forum-post.msg.deleted=Node {0} deleted +forum-post.msg.marked.removed=Node {0} marked as removed diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_de.properties b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_de.properties new file mode 100755 index 0000000000..33122da30d --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_de.properties @@ -0,0 +1,2 @@ +forum-post.msg.deleted=Node {0} gel\u00f6scht +forum-post.msg.marked.removed=Node {0} als entfernt gekennzeichnet diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_es.properties b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_es.properties new file mode 100755 index 0000000000..b954e60984 --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_es.properties @@ -0,0 +1,2 @@ +forum-post.msg.deleted=Nodo {0} eliminado +forum-post.msg.marked.removed=Nodo {0} marcado como eliminado diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_fr.properties b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_fr.properties new file mode 100755 index 0000000000..d97d6c2b19 --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_fr.properties @@ -0,0 +1,2 @@ +forum-post.msg.deleted=N\u0153ud {0} supprim\u00e9 +forum-post.msg.marked.removed=N\u0153ud {0} marqu\u00e9 comme supprim\u00e9 diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_it.properties b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_it.properties new file mode 100755 index 0000000000..693eb2f438 --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_it.properties @@ -0,0 +1,2 @@ +forum-post.msg.deleted=Nodo {0} eliminato +forum-post.msg.marked.removed=Nodo {0} contrassegnato come rimosso diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_ja.properties b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_ja.properties new file mode 100755 index 0000000000..d218214116 --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_ja.properties @@ -0,0 +1,2 @@ +forum-post.msg.deleted=\u30ce\u30fc\u30c9 ''{0}'' \u304c\u524a\u9664\u3055\u308c\u307e\u3057\u305f +forum-post.msg.marked.removed=\u30ce\u30fc\u30c9 "{0}" \u304c\u524a\u9664\u6e08\u307f\u3068\u3057\u3066\u30de\u30fc\u30af\u3055\u308c\u307e\u3057\u305f diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_nb.properties b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_nb.properties new file mode 100755 index 0000000000..ea419fe8e4 --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_nb.properties @@ -0,0 +1,2 @@ +forum-post.msg.deleted=Slettet node {0} +forum-post.msg.marked.removed=Node {0} er merket som slettet diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_nl.properties b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_nl.properties new file mode 100755 index 0000000000..8a0d1d645f --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_nl.properties @@ -0,0 +1,2 @@ +forum-post.msg.deleted=Node {0} verwijderd +forum-post.msg.marked.removed=Node {0} gemarkeerd als verwijderd diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_pt_BR.properties b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_pt_BR.properties new file mode 100644 index 0000000000..c1d8e916ff --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_pt_BR.properties @@ -0,0 +1,2 @@ +forum-post.msg.deleted=N\u00f3 {0} exclu\u00eddo +forum-post.msg.marked.removed=N\u00f3 {0} marcado como removido diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_ru.properties b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_ru.properties new file mode 100755 index 0000000000..4d6b255554 --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_ru.properties @@ -0,0 +1,2 @@ +forum-post.msg.deleted=\u0423\u0437\u0435\u043b {0} \u0443\u0434\u0430\u043b\u0435\u043d +forum-post.msg.marked.removed=\u0423\u0437\u0435\u043b {0} \u043e\u0442\u043c\u0435\u0447\u0435\u043d \u043a\u0430\u043a \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_zh_CN.properties b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_zh_CN.properties new file mode 100755 index 0000000000..39bebedffd --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.delete_zh_CN.properties @@ -0,0 +1,2 @@ +forum-post.msg.deleted=\u5df2\u5220\u9664\u8282\u70b9 {0} +forum-post.msg.marked.removed=\u8282\u70b9 {0} \u6807\u8bb0\u4e3a\u5df2\u79fb\u9664 diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.get.desc.xml b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.get.desc.xml new file mode 100644 index 0000000000..f011745e9d --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.get.desc.xml @@ -0,0 +1,10 @@ + + Get topic details + Gets the details for a topic. + /api/forum/post/site/{site}/{container}/{path} + /api/forum/post/node/{store_type}/{store_id}/{id} + argument + user + required + limited_support + diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.get.json.ftl b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.get.json.ftl new file mode 100644 index 0000000000..a3cd1be869 --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.get.json.ftl @@ -0,0 +1,4 @@ +<#import "../post.lib.ftl" as postLib /> +{ + "item": <@postLib.postJSON postData=postData /> +} \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.put.desc.xml b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.put.desc.xml new file mode 100644 index 0000000000..c04df8dfa7 --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.put.desc.xml @@ -0,0 +1,10 @@ + + Update topic + Updates a topic. + /api/forum/post/site/{site}/{container}/{path} + /api/forum/post/node/{store_type}/{store_id}/{id} + argument + user + required + limited_support + \ No newline at end of file diff --git a/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.put.json.ftl b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.put.json.ftl new file mode 100644 index 0000000000..a3cd1be869 --- /dev/null +++ b/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/discussions/posts/forum-post.put.json.ftl @@ -0,0 +1,4 @@ +<#import "../post.lib.ftl" as postLib /> +{ + "item": <@postLib.postJSON postData=postData /> +} \ No newline at end of file diff --git a/src/main/resources/alfresco/web-scripts-application-context.xml b/src/main/resources/alfresco/web-scripts-application-context.xml index 151f7df1c6..7bf6b4e3b7 100644 --- a/src/main/resources/alfresco/web-scripts-application-context.xml +++ b/src/main/resources/alfresco/web-scripts-application-context.xml @@ -1329,6 +1329,89 @@ parent="abstractLinksWebScript">
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/java/org/alfresco/AppContext04TestSuite.java b/src/test/java/org/alfresco/AppContext04TestSuite.java index 0a7e749815..7d4895f3ad 100644 --- a/src/test/java/org/alfresco/AppContext04TestSuite.java +++ b/src/test/java/org/alfresco/AppContext04TestSuite.java @@ -42,6 +42,7 @@ import org.junit.runners.Suite; org.alfresco.repo.web.scripts.audit.AuditWebScriptTest.class, org.alfresco.repo.web.scripts.blogs.BlogServiceTest.class, org.alfresco.repo.web.scripts.dictionary.DictionaryRestApiTest.class, + org.alfresco.repo.web.scripts.discussion.DiscussionRestApiTest.class, org.alfresco.repo.web.scripts.activities.feed.control.FeedControlTest.class, org.alfresco.repo.web.scripts.forms.FormRestApiGet_Test.class, org.alfresco.repo.web.scripts.forms.FormRestApiJsonPost_Test.class, diff --git a/src/test/java/org/alfresco/repo/web/scripts/ReadOnlyTransactionInGetRestApiTest.java b/src/test/java/org/alfresco/repo/web/scripts/ReadOnlyTransactionInGetRestApiTest.java index 022edcd1e6..372d4c3a28 100644 --- a/src/test/java/org/alfresco/repo/web/scripts/ReadOnlyTransactionInGetRestApiTest.java +++ b/src/test/java/org/alfresco/repo/web/scripts/ReadOnlyTransactionInGetRestApiTest.java @@ -1,28 +1,28 @@ -/* - * #%L - * Alfresco Remote API - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ package org.alfresco.repo.web.scripts; import java.util.List; @@ -58,6 +58,7 @@ public class ReadOnlyTransactionInGetRestApiTest extends BaseWebScriptTest private static final String TEST_SITE_NAME = "readOnlyTestSite"; private static final String URL_GET_SITE_BLOG = "/api/blog/site/" + TEST_SITE_NAME + "/blog"; + private static final String URL_GET_SITE_FORUM_POSTS = "/api/forum/site/" + TEST_SITE_NAME + "/discussions/posts"; private static final String URL_GET_SITE_LINKS = "/api/links/site/" + TEST_SITE_NAME + "/links?page=1&pageSize=10"; private static final String URL_GET_SITE_LINK = "/api/links/link/site/" + TEST_SITE_NAME + "/links/123456789"; private static final String URL_GET_SITE_TAGS = "/api/tagscopes/site/" + TEST_SITE_NAME + "/tags"; @@ -141,6 +142,13 @@ public class ReadOnlyTransactionInGetRestApiTest extends BaseWebScriptTest assertEquals(Status.STATUS_OK, response.getStatus()); } + public void testGetSiteForumPosts() throws Exception + { + Response response = sendRequest(new GetRequest(URL_GET_SITE_FORUM_POSTS), 200); + logResponse(response); + assertEquals(Status.STATUS_OK, response.getStatus()); + } + public void testGetSiteLinks() throws Exception { Response response = sendRequest(new GetRequest(URL_GET_SITE_LINKS), 200); diff --git a/src/test/java/org/alfresco/repo/web/scripts/WebScriptTestSuite.java b/src/test/java/org/alfresco/repo/web/scripts/WebScriptTestSuite.java index 509a5ecf4f..a74d4d4a57 100644 --- a/src/test/java/org/alfresco/repo/web/scripts/WebScriptTestSuite.java +++ b/src/test/java/org/alfresco/repo/web/scripts/WebScriptTestSuite.java @@ -37,6 +37,7 @@ import org.alfresco.repo.web.scripts.blogs.BlogServiceTest; import org.alfresco.repo.web.scripts.comment.CommentsApiTest; import org.alfresco.repo.web.scripts.custommodel.CustomModelImportTest; import org.alfresco.repo.web.scripts.dictionary.DictionaryRestApiTest; +import org.alfresco.repo.web.scripts.discussion.DiscussionRestApiTest; import org.alfresco.repo.web.scripts.facet.FacetRestApiTest; import org.alfresco.repo.web.scripts.forms.FormRestApiGet_Test; import org.alfresco.repo.web.scripts.forms.FormRestApiJsonPost_Test; @@ -81,6 +82,7 @@ public class WebScriptTestSuite extends TestSuite suite.addTestSuite( AuditWebScriptTest.class ); suite.addTestSuite( BlogServiceTest.class ); suite.addTestSuite( DictionaryRestApiTest.class ); + suite.addTestSuite( DiscussionRestApiTest.class ); suite.addTestSuite( FeedControlTest.class ); suite.addTestSuite( FormRestApiGet_Test.class ); suite.addTestSuite( FormRestApiJsonPost_Test.class ); diff --git a/src/test/java/org/alfresco/repo/web/scripts/discussion/DiscussionRestApiTest.java b/src/test/java/org/alfresco/repo/web/scripts/discussion/DiscussionRestApiTest.java new file mode 100644 index 0000000000..2a178643fb --- /dev/null +++ b/src/test/java/org/alfresco/repo/web/scripts/discussion/DiscussionRestApiTest.java @@ -0,0 +1,1306 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.web.scripts.discussion; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.transaction.UserTransaction; + +import org.alfresco.model.ContentModel; +import org.alfresco.model.ForumModel; +import org.alfresco.repo.node.archive.NodeArchiveService; +import org.alfresco.repo.policy.BehaviourFilter; +import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.site.SiteModel; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.repo.web.scripts.BaseWebScriptTest; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +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.alfresco.service.cmr.site.SiteVisibility; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.GUID; +import org.alfresco.util.PropertyMap; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONArray; +import org.json.JSONObject; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.TestWebScriptServer.DeleteRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.PostRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.PutRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.Response; + +/** + * Unit Test to test Discussions Web Script API + */ +public class DiscussionRestApiTest extends BaseWebScriptTest +{ + @SuppressWarnings("unused") + private static Log logger = LogFactory.getLog(DiscussionRestApiTest.class); + + private static final String DELETED_REPLY_POST_MARKER = "[[deleted]]"; + + private MutableAuthenticationService authenticationService; + private AuthenticationComponent authenticationComponent; + private TransactionService transactionService; + private BehaviourFilter policyBehaviourFilter; + private PermissionService permissionService; + private PersonService personService; + private SiteService siteService; + private NodeService nodeService; + private NodeService internalNodeService; + private NodeArchiveService nodeArchiveService; + + private static final String USER_ONE = "UserOneThird"; + private static final String USER_TWO = "UserTwoThird"; + private static final String SITE_SHORT_NAME_DISCUSSION = "DiscussionSiteShortNameThree"; + private static final String COMPONENT_DISCUSSION = "discussions"; + + private static final String URL_FORUM_SITE_POST = "/api/forum/post/site/" + SITE_SHORT_NAME_DISCUSSION + "/" + COMPONENT_DISCUSSION + "/"; + private static final String URL_FORUM_SITE_POSTS = "/api/forum/site/" + SITE_SHORT_NAME_DISCUSSION + "/" + COMPONENT_DISCUSSION + "/posts"; + private static final String URL_FORUM_NODE_POST_BASE = "/api/forum/post/node/"; // Plus node id + private static final String URL_FORUM_NODE_POSTS_BASE = "/api/forum/node/"; // Plus node id + /posts + + private List posts = new ArrayList(5); + private NodeRef FORUM_NODE; + + + // General methods + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + this.authenticationService = (MutableAuthenticationService)getServer().getApplicationContext().getBean("AuthenticationService"); + this.authenticationComponent = (AuthenticationComponent)getServer().getApplicationContext().getBean("authenticationComponent"); + this.policyBehaviourFilter = (BehaviourFilter)getServer().getApplicationContext().getBean("policyBehaviourFilter"); + this.transactionService = (TransactionService)getServer().getApplicationContext().getBean("transactionService"); + this.permissionService = (PermissionService)getServer().getApplicationContext().getBean("PermissionService"); + this.personService = (PersonService)getServer().getApplicationContext().getBean("PersonService"); + this.siteService = (SiteService)getServer().getApplicationContext().getBean("SiteService"); + this.nodeService = (NodeService)getServer().getApplicationContext().getBean("NodeService"); + this.internalNodeService = (NodeService)getServer().getApplicationContext().getBean("nodeService"); + this.nodeArchiveService = (NodeArchiveService)getServer().getApplicationContext().getBean("nodeArchiveService"); + + // Authenticate as user + this.authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName()); + + // Create test site + // - only create the site if it doesn't already exist + SiteInfo siteInfo = this.siteService.getSite(SITE_SHORT_NAME_DISCUSSION); + if (siteInfo == null) + { + siteInfo = this.siteService.createSite("DiscussionSitePreset", SITE_SHORT_NAME_DISCUSSION, + "DiscussionSiteTitle", "DiscussionSiteDescription", SiteVisibility.PUBLIC); + } + final NodeRef siteNodeRef = siteInfo.getNodeRef(); + + // Create the forum + final String forumNodeName = "TestForum"; + FORUM_NODE = nodeService.getChildByName(siteInfo.getNodeRef(), ContentModel.ASSOC_CONTAINS, forumNodeName); + if (FORUM_NODE == null) + { + FORUM_NODE = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() { + @Override + public NodeRef execute() throws Throwable { + Map props = new HashMap(5); + props.put(ContentModel.PROP_NAME, forumNodeName); + props.put(ContentModel.PROP_TITLE, forumNodeName); + + return nodeService.createNode( + siteNodeRef, ContentModel.ASSOC_CONTAINS, + QName.createQName(forumNodeName), ForumModel.TYPE_FORUM, props + ).getChildRef(); + } + }); + } + + // Create users + createUser(USER_ONE, SiteModel.SITE_COLLABORATOR, SITE_SHORT_NAME_DISCUSSION); + createUser(USER_TWO, SiteModel.SITE_CONTRIBUTOR, SITE_SHORT_NAME_DISCUSSION); + + // Do tests as inviter user + this.authenticationComponent.setCurrentUser(USER_ONE); + } + + @Override + protected void tearDown() throws Exception + { + super.tearDown(); + + // admin user required to delete user + this.authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName()); + + // delete the discussions users + if(personService.personExists(USER_ONE)) + { + personService.deletePerson(USER_ONE); + } + if (this.authenticationService.authenticationExists(USER_ONE)) + { + this.authenticationService.deleteAuthentication(USER_ONE); + } + + if(personService.personExists(USER_TWO)) + { + personService.deletePerson(USER_TWO); + } + if (this.authenticationService.authenticationExists(USER_TWO)) + { + this.authenticationService.deleteAuthentication(USER_TWO); + } + + SiteInfo siteInfo = this.siteService.getSite(SITE_SHORT_NAME_DISCUSSION); + if (siteInfo != null) + { + // delete discussions test site + RetryingTransactionCallback deleteCallback = new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + siteService.deleteSite(SITE_SHORT_NAME_DISCUSSION); + return null; + } + }; + transactionService.getRetryingTransactionHelper().doInTransaction(deleteCallback); + nodeArchiveService.purgeArchivedNode(nodeArchiveService.getArchivedNode(siteInfo.getNodeRef())); + } + } + + private void createUser(String userName, String role, String siteName) + { + // if user with given user name doesn't already exist then create user + if (!this.authenticationService.authenticationExists(userName)) + { + // create user + this.authenticationService.createAuthentication(userName, "password".toCharArray()); + } + + if (!this.personService.personExists(userName)) + { + // create person properties + PropertyMap personProps = new PropertyMap(); + personProps.put(ContentModel.PROP_USERNAME, userName); + personProps.put(ContentModel.PROP_FIRSTNAME, "FirstName123"); + personProps.put(ContentModel.PROP_LASTNAME, "LastName123"); + personProps.put(ContentModel.PROP_EMAIL, "FirstName123.LastName123@email.com"); + personProps.put(ContentModel.PROP_JOBTITLE, "JobTitle123"); + personProps.put(ContentModel.PROP_JOBTITLE, "Organisation123"); + + // create person node for user + this.personService.createPerson(personProps); + } + + // add the user as a member with the given role + this.siteService.setMembership(siteName, userName, role); + + // Give the test user access to the test node + // They need to be able to read it, and create children of it + permissionService.setPermission(FORUM_NODE, userName, PermissionService.READ, true); + permissionService.setPermission(FORUM_NODE, userName, PermissionService.CREATE_CHILDREN, true); + } + + + // ----------------------------------------------------- + // Test helper methods + // ----------------------------------------------------- + + /** + * Creates a new topic+post in the test site + */ + private JSONObject createSitePost(String title, String content, int expectedStatus) + throws Exception + { + return doCreatePost(URL_FORUM_SITE_POSTS, title, content, expectedStatus); + } + + /** + * Creates a new topic+post under the given node + */ + private JSONObject createNodePost(NodeRef nodeRef, String title, String content, + int expectedStatus) throws Exception + { + return doCreatePost(getPostsUrl(nodeRef), title, content, expectedStatus); + } + + private JSONObject doCreatePost(String url, String title, String content, + int expectedStatus) throws Exception + { + JSONObject post = new JSONObject(); + post.put("title", title); + post.put("content", content); + Response response = sendRequest(new PostRequest(url, post.toString(), "application/json"), expectedStatus); + + if (expectedStatus != Status.STATUS_OK) + { + return null; + } + + JSONObject result = new JSONObject(response.getContentAsString()); + JSONObject item = result.getJSONObject("item"); + posts.add(item.getString("name")); + return item; + } + + private JSONObject updatePost(NodeRef nodeRef, String title, String content, + int expectedStatus) throws Exception + { + return doUpdatePost(getPostUrl(nodeRef), title, content, expectedStatus); + } + + private JSONObject updatePost(String name, String title, String content, + int expectedStatus) throws Exception + { + return doUpdatePost(URL_FORUM_SITE_POST + name, title, content, expectedStatus); + } + + private JSONObject doUpdatePost(String url, String title, String content, + int expectedStatus) throws Exception + { + JSONObject post = new JSONObject(); + post.put("title", title); + post.put("content", content); + Response response = sendRequest(new PutRequest(url, post.toString(), "application/json"), expectedStatus); + + if (expectedStatus != Status.STATUS_OK) + { + return null; + } + + JSONObject result = new JSONObject(response.getContentAsString()); + return result.getJSONObject("item"); + } + + private JSONObject getPost(String name, int expectedStatus) throws Exception + { + return doGetPost(URL_FORUM_SITE_POST + name, expectedStatus); + } + + private JSONObject getPost(NodeRef nodeRef, int expectedStatus) throws Exception + { + return doGetPost(getPostUrl(nodeRef), expectedStatus); + } + + private JSONObject doGetPost(String url, int expectedStatus) throws Exception + { + Response response = sendRequest(new GetRequest(url), expectedStatus); + if (expectedStatus == Status.STATUS_OK) + { + JSONObject result = new JSONObject(response.getContentAsString()); + return result.getJSONObject("item"); + } + else + { + return null; + } + } + + private JSONObject getReplies(String name, int expectedStatus) throws Exception + { + return doGetReplies(getRepliesUrl(name), expectedStatus); + } + + private JSONObject getReplies(NodeRef nodeRef, int expectedStatus) throws Exception + { + return doGetReplies(getRepliesUrl(nodeRef), expectedStatus); + } + + private JSONObject doGetReplies(String url, int expectedStatus) throws Exception + { + Response response = sendRequest(new GetRequest(url), expectedStatus); + if (expectedStatus == Status.STATUS_OK) + { + JSONObject result = new JSONObject(response.getContentAsString()); + return result; + } + else + { + return null; + } + } + + private JSONObject getPosts(String type, int expectedStatus) throws Exception + { + return doGetPosts(URL_FORUM_SITE_POSTS, type, expectedStatus); + } + + private JSONObject getPosts(NodeRef nodeRef, String type, int expectedStatus) throws Exception + { + return doGetPosts(getPostsUrl(nodeRef), type, expectedStatus); + } + + private JSONObject doGetPosts(String baseUrl, String type, int expectedStatus) throws Exception + { + String url = null; + if (type == null) + { + url = baseUrl; + } + else if (type == "limit") + { + url = baseUrl + "?pageSize=1"; + } + else if (type == "hot") + { + url = baseUrl + "/hot"; + } + else if (type == "mine") + { + url = baseUrl + "/myposts"; + } + else if (type.startsWith("new")) + { + url = baseUrl + "/" + type; + } + else + { + throw new IllegalArgumentException("Invalid search type " + type); + } + + Response response = sendRequest(new GetRequest(url), expectedStatus); + if (expectedStatus == Status.STATUS_OK) + { + JSONObject result = new JSONObject(response.getContentAsString()); + return result; + } + else + { + return null; + } + } + + private JSONObject deletePost(String name, int expectedStatus) throws Exception + { + return doDeletePost(URL_FORUM_SITE_POST + name, expectedStatus); + } + + private JSONObject deletePost(NodeRef nodeRef, int expectedStatus) throws Exception + { + return doDeletePost(getPostUrl(nodeRef), expectedStatus); + } + + private JSONObject doDeletePost(String url, int expectedStatus) throws Exception + { + Response response = sendRequest(new DeleteRequest(url), Status.STATUS_OK); + if (expectedStatus == Status.STATUS_OK) + { + return new JSONObject(response.getContentAsString()); + } + else + { + return null; + } + } + + private String getRepliesUrl(NodeRef nodeRef) + { + return getPostUrl(nodeRef) + "/replies"; + } + + private String getRepliesUrl(String postName) + { + return URL_FORUM_SITE_POST + postName + "/replies"; + } + + private String getPostUrl(NodeRef nodeRef) + { + return URL_FORUM_NODE_POST_BASE + nodeRef.toString().replace("://", "/"); + } + + private String getPostsUrl(NodeRef nodeRef) + { + return URL_FORUM_NODE_POSTS_BASE + nodeRef.toString().replace("://", "/") + "/posts"; + } + + private JSONObject createReply(NodeRef nodeRef, String title, String content, int expectedStatus) + throws Exception + { + JSONObject reply = new JSONObject(); + reply.put("title", title); + reply.put("content", content); + Response response = sendRequest(new PostRequest(getRepliesUrl(nodeRef), reply.toString(), "application/json"), expectedStatus); + + if (expectedStatus != 200) + { + return null; + } + + JSONObject result = new JSONObject(response.getContentAsString()); + return result.getJSONObject("item"); + } + + private JSONObject updateComment(NodeRef nodeRef, String title, String content, + int expectedStatus) throws Exception + { + JSONObject comment = new JSONObject(); + comment.put("title", title); + comment.put("content", content); + Response response = sendRequest(new PutRequest(getPostUrl(nodeRef), comment.toString(), "application/json"), expectedStatus); + + if (expectedStatus != Status.STATUS_OK) + { + return null; + } + + //logger.debug("Comment updated: " + response.getContentAsString()); + JSONObject result = new JSONObject(response.getContentAsString()); + return result.getJSONObject("item"); + } + + /** + * Monkeys with the created and published dates on a topic+posts + */ + private void pushCreatedDateBack(NodeRef node, int daysAgo) throws Exception + { + Date created = (Date)nodeService.getProperty(node, ContentModel.PROP_CREATED); + Date newCreated = new Date(created.getTime() - daysAgo*24*60*60*1000); + Date published = (Date)nodeService.getProperty(node, ContentModel.PROP_PUBLISHED); + if(published == null) published = created; + Date newPublished = new Date(published.getTime() - daysAgo*24*60*60*1000); + + UserTransaction txn = transactionService.getUserTransaction(); + txn.begin(); + + this.policyBehaviourFilter.disableBehaviour(ContentModel.ASPECT_AUDITABLE); + internalNodeService.setProperty(node, ContentModel.PROP_CREATED, newCreated); + internalNodeService.setProperty(node, ContentModel.PROP_MODIFIED, newCreated); + internalNodeService.setProperty(node, ContentModel.PROP_PUBLISHED, newPublished); + this.policyBehaviourFilter.enableBehaviour(ContentModel.ASPECT_AUDITABLE); + + txn.commit(); + + // Now chance something else on the node to have it re-indexed + nodeService.setProperty(node, ContentModel.PROP_CREATED, newCreated); + nodeService.setProperty(node, ContentModel.PROP_MODIFIED, newCreated); + nodeService.setProperty(node, ContentModel.PROP_PUBLISHED, newPublished); + nodeService.setProperty(node, ContentModel.PROP_DESCRIPTION, "Forced change"); + + // Finally change any children (eg if updating a topic, do the posts) + for(ChildAssociationRef ref : nodeService.getChildAssocs(node)) + { + pushCreatedDateBack(ref.getChildRef(), daysAgo); + } + } + + // ----------------------------------------------------- + // Tests + // ----------------------------------------------------- + + public void testCreateForumPost() throws Exception + { + String title = "test"; + String content = "test"; + JSONObject item = createSitePost(title, content, Status.STATUS_OK); + + // Check that the values in the response are correct + assertEquals(title, item.get("title")); + assertEquals(content, item.get("content")); + assertEquals(0, item.get("replyCount")); + assertEquals("Invalid JSON " + item, true, item.has("createdOn")); + assertEquals("Invalid JSON " + item, true, item.has("modifiedOn")); + assertEquals("Invalid JSON " + item, true, item.has("author")); + assertEquals("Invalid JSON " + item, true, item.has("permissions")); + assertEquals("Invalid JSON " + item, true, item.has("url")); + assertEquals("Invalid JSON " + item, true, item.has("repliesUrl")); + assertEquals("Invalid JSON " + item, true, item.has("nodeRef")); + + // Save some details + String name = item.getString("name"); + NodeRef nodeRef = new NodeRef(item.getString("nodeRef")); + + + // Fetch the post by name and check + item = getPost(name, Status.STATUS_OK); + + assertEquals(title, item.get("title")); + assertEquals(content, item.get("content")); + assertEquals(0, item.get("replyCount")); + assertEquals("Invalid JSON " + item, true, item.has("createdOn")); + assertEquals("Invalid JSON " + item, true, item.has("modifiedOn")); + assertEquals("Invalid JSON " + item, true, item.has("author")); + assertEquals("Invalid JSON " + item, true, item.has("permissions")); + assertEquals("Invalid JSON " + item, true, item.has("url")); + assertEquals("Invalid JSON " + item, true, item.has("repliesUrl")); + assertEquals("Invalid JSON " + item, true, item.has("nodeRef")); + + + // Fetch the post by noderef and check + item = getPost(nodeRef, Status.STATUS_OK); + + assertEquals(title, item.get("title")); + assertEquals(content, item.get("content")); + assertEquals(0, item.get("replyCount")); + assertEquals("Invalid JSON " + item, true, item.has("createdOn")); + assertEquals("Invalid JSON " + item, true, item.has("modifiedOn")); + assertEquals("Invalid JSON " + item, true, item.has("author")); + assertEquals("Invalid JSON " + item, true, item.has("permissions")); + assertEquals("Invalid JSON " + item, true, item.has("url")); + assertEquals("Invalid JSON " + item, true, item.has("repliesUrl")); + assertEquals("Invalid JSON " + item, true, item.has("nodeRef")); + + + // Create another post, this time by noderef + title = "By Node Title"; + content = "By Node Content"; + item = createNodePost(FORUM_NODE, title, content, Status.STATUS_OK); + + assertEquals(title, item.get("title")); + assertEquals(content, item.get("content")); + assertEquals(0, item.get("replyCount")); + + // Check it by noderef + nodeRef = new NodeRef(item.getString("nodeRef")); + item = getPost(nodeRef, Status.STATUS_OK); + + assertEquals(title, item.get("title")); + assertEquals(content, item.get("content")); + assertEquals(0, item.get("replyCount")); + } + + public void testUpdateForumPost() throws Exception + { + String title = "test"; + String content = "test"; + JSONObject item = createSitePost(title, content, 200); + + // check that the values + assertEquals(title, item.get("title")); + assertEquals(content, item.get("content")); + assertEquals(false, item.getBoolean("isUpdated")); + + assertEquals(true, item.has("name")); + String name = item.getString("name"); + assertEquals(true, item.has("nodeRef")); + NodeRef nodeRef = new NodeRef(item.getString("nodeRef")); + + // fetch the post by name + item = getPost(item.getString("name"), 200); + assertEquals(title, item.get("title")); + assertEquals(content, item.get("content")); + assertEquals(false, item.getBoolean("isUpdated")); + + // Fetch the post by noderef + item = getPost(nodeRef, 200); + assertEquals(title, item.get("title")); + assertEquals(content, item.get("content")); + assertEquals(false, item.getBoolean("isUpdated")); + + + // Update it by name + String title2 = "updated test"; + String content2 = "test updated"; + item = updatePost(name, title2, content2, 200); + + // Check the response + assertEquals(title2, item.get("title")); + assertEquals(content2, item.get("content")); + assertEquals(name, item.get("name")); + assertEquals(nodeRef.toString(), item.get("nodeRef")); + assertEquals(true, item.getBoolean("isUpdated")); + + // Fetch and check + item = getPost(nodeRef, 200); + assertEquals(title2, item.get("title")); + assertEquals(content2, item.get("content")); + assertEquals(name, item.get("name")); + assertEquals(nodeRef.toString(), item.get("nodeRef")); + assertEquals(true, item.getBoolean("isUpdated")); + + + // Update it again, this time by noderef + String title3 = "updated 3 test"; + String content3 = "test 3 updated"; + item = updatePost(nodeRef, title3, content3, 200); + + // Check that the values returned are correct + assertEquals(title3, item.get("title")); + assertEquals(content3, item.get("content")); + assertEquals(name, item.get("name")); + assertEquals(nodeRef.toString(), item.get("nodeRef")); + assertEquals(true, item.getBoolean("isUpdated")); + + // Fetch and re-check + item = getPost(nodeRef, 200); + assertEquals(title3, item.get("title")); + assertEquals(content3, item.get("content")); + assertEquals(name, item.get("name")); + assertEquals(nodeRef.toString(), item.get("nodeRef")); + assertEquals(true, item.getBoolean("isUpdated")); + } + + /** + * Tests that the permissions details included with topics and + * posts are correct + */ + public void testPermissions() throws Exception + { + // Create a post, and check the details on it + JSONObject item = createSitePost("test", "test", Status.STATUS_OK); + String name = item.getString("name"); + + JSONObject perms = item.getJSONObject("permissions"); + assertEquals(true, perms.getBoolean("edit")); + assertEquals(true, perms.getBoolean("reply")); + assertEquals(true, perms.getBoolean("delete")); + + // Check on a fetch too + item = getPost(name, Status.STATUS_OK); + perms = item.getJSONObject("permissions"); + assertEquals(true, perms.getBoolean("edit")); + assertEquals(true, perms.getBoolean("reply")); + assertEquals(true, perms.getBoolean("delete")); + + + // Switch to another user, see what they see + this.authenticationComponent.setCurrentUser(USER_TWO); + + item = getPost(name, Status.STATUS_OK); + perms = item.getJSONObject("permissions"); + assertEquals(false, perms.getBoolean("edit")); + assertEquals(true, perms.getBoolean("reply")); + assertEquals(false, perms.getBoolean("delete")); + + + // Remove the user from the site, see the change + this.siteService.removeMembership(SITE_SHORT_NAME_DISCUSSION, USER_TWO); + + item = getPost(name, Status.STATUS_OK); + perms = item.getJSONObject("permissions"); + assertEquals(false, perms.getBoolean("edit")); + assertEquals(false, perms.getBoolean("reply")); + assertEquals(false, perms.getBoolean("delete")); + + + // Make the site private, will vanish + SiteInfo siteInfo = siteService.getSite(SITE_SHORT_NAME_DISCUSSION); + siteInfo.setVisibility(SiteVisibility.PRIVATE); + this.authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName()); + this.siteService.updateSite(siteInfo); + this.authenticationComponent.setCurrentUser(USER_TWO); + + // On a private site we're not a member of, shouldn't be visable at all + getPost(name, Status.STATUS_NOT_FOUND); + } + + /** + * ALF-1973 - If the user who added a reply has been deleted, don't break + */ + public void testViewReplyByDeletedUser() throws Exception + { + // Create a post + JSONObject item = createSitePost("test", "test", Status.STATUS_OK); + String name = item.getString("name"); + NodeRef topicNodeRef = new NodeRef(item.getString("nodeRef")); + + // Now create a reply as a different user + this.authenticationComponent.setCurrentUser(USER_TWO); + createReply(topicNodeRef, "Reply", "By the other user", Status.STATUS_OK); + + // Should see the reply + item = getReplies(name, Status.STATUS_OK); + assertEquals(1, item.getJSONArray("items").length()); + + // Delete the user, check that the reply still shows + this.authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName()); + personService.deletePerson(USER_TWO); + this.authenticationComponent.setCurrentUser(USER_ONE); + + item = getReplies(name, Status.STATUS_OK); + assertEquals(1, item.getJSONArray("items").length()); + } + + public void testAddReply() throws Exception + { + // Create a root post + JSONObject item = createSitePost("test", "test", Status.STATUS_OK); + String topicName = item.getString("name"); + NodeRef topicNodeRef = new NodeRef(item.getString("nodeRef")); + + // Add a reply + JSONObject reply = createReply(topicNodeRef, "test", "test", Status.STATUS_OK); + NodeRef replyNodeRef = new NodeRef(reply.getString("nodeRef")); + assertEquals("test", reply.getString("title")); + assertEquals("test", reply.getString("content")); + + // Add a reply to the reply + JSONObject reply2 = createReply(replyNodeRef, "test2", "test2", 200); + NodeRef reply2NodeRef = new NodeRef(reply2.getString("nodeRef")); + assertEquals("test2", reply2.getString("title")); + assertEquals("test2", reply2.getString("content")); + + + // Check things were correctly setup. These should all be siblings + // of each other, with relations between the replies + assertEquals(ForumModel.TYPE_TOPIC, nodeService.getType(topicNodeRef)); + assertEquals(ForumModel.TYPE_POST, nodeService.getType(replyNodeRef)); + assertEquals(ForumModel.TYPE_POST, nodeService.getType(reply2NodeRef)); + assertEquals(topicNodeRef, nodeService.getPrimaryParent(replyNodeRef).getParentRef()); + assertEquals(topicNodeRef, nodeService.getPrimaryParent(reply2NodeRef).getParentRef()); + + // Reply 2 should have an assoc to Reply 1 + assertEquals(0, nodeService.getSourceAssocs(reply2NodeRef, RegexQNamePattern.MATCH_ALL).size()); + assertEquals(1, nodeService.getTargetAssocs(reply2NodeRef, RegexQNamePattern.MATCH_ALL).size()); + assertEquals(replyNodeRef, nodeService.getTargetAssocs(reply2NodeRef, RegexQNamePattern.MATCH_ALL).get(0).getTargetRef()); + + assertEquals(1, nodeService.getSourceAssocs(replyNodeRef, RegexQNamePattern.MATCH_ALL).size()); + assertEquals(1, nodeService.getTargetAssocs(replyNodeRef, RegexQNamePattern.MATCH_ALL).size()); + assertEquals(reply2NodeRef, nodeService.getSourceAssocs(replyNodeRef, RegexQNamePattern.MATCH_ALL).get(0).getSourceRef()); + + + // Fetch all replies for the post + JSONObject result = getReplies(topicNodeRef, Status.STATUS_OK); + // check the number of replies + assertEquals(1, result.getJSONArray("items").length()); + + // Check the replies by name too + result = getReplies(topicName, Status.STATUS_OK); + assertEquals(1, result.getJSONArray("items").length()); + + + // Fetch the top level post again, and check the counts there + // That post should have one direct reply, and one reply to it's reply + item = getPost(topicName, Status.STATUS_OK); + assertEquals(2, item.getInt("totalReplyCount")); + assertEquals(1, item.getInt("replyCount")); + } + + public void testUpdateReply() throws Exception + { + // Create a root post + JSONObject item = createSitePost("test", "test", Status.STATUS_OK); + String postName = item.getString("name"); + NodeRef postNodeRef = new NodeRef(item.getString("nodeRef")); + assertEquals("test", item.getString("title")); + assertEquals("test", item.getString("content")); + assertEquals(false, item.getBoolean("isUpdated")); + + + // Add a reply to it + JSONObject reply = createReply(postNodeRef, "rtest", "rtest", Status.STATUS_OK); + NodeRef replyNodeRef = new NodeRef(reply.getString("nodeRef")); + assertEquals("rtest", reply.getString("title")); + assertEquals("rtest", reply.getString("content")); + assertEquals(false, reply.getBoolean("isUpdated")); + + + // Now update the reply + JSONObject reply2 = updatePost(replyNodeRef, "test2", "test2", Status.STATUS_OK); + assertEquals("test2", reply2.getString("title")); + assertEquals("test2", reply2.getString("content")); + assertEquals(true, reply2.getBoolean("isUpdated")); + + // Fetch it to check + reply2 = getPost(replyNodeRef, Status.STATUS_OK); + assertEquals("test2", reply2.getString("title")); + assertEquals("test2", reply2.getString("content")); + assertEquals(true, reply2.getBoolean("isUpdated")); + + + // Ensure the original post wasn't changed + item = getPost(postName, Status.STATUS_OK); + assertEquals("test", item.getString("title")); + assertEquals("test", item.getString("content")); + assertEquals(false, item.getBoolean("isUpdated")); + } + + public void testDeleteToplevelPost() throws Exception + { + // Create two posts + JSONObject item1 = createSitePost("test1", "test1", Status.STATUS_OK); + JSONObject item2 = createSitePost("test2", "test2", Status.STATUS_OK); + String name1 = item1.getString("name"); + NodeRef nodeRef1 = new NodeRef(item1.getString("nodeRef")); + NodeRef nodeRef2 = new NodeRef(item2.getString("nodeRef")); + + // The node references returned correspond to the topics + assertEquals(ForumModel.TYPE_TOPIC, nodeService.getType(nodeRef1)); + assertEquals(ForumModel.TYPE_TOPIC, nodeService.getType(nodeRef2)); + + + // Delete one post by name + deletePost(name1, Status.STATUS_OK); + + // Check it went + getPost(name1, Status.STATUS_NOT_FOUND); + + + // Delete the other post by noderef + deletePost(nodeRef2, Status.STATUS_OK); + + // Check it went + getPost(nodeRef2, Status.STATUS_NOT_FOUND); + + + // Check all the nodes have gone + assertEquals(false, nodeService.exists(nodeRef1)); + assertEquals(false, nodeService.exists(nodeRef2)); + } + + public void testDeleteReplyPost() throws Exception + { + // Create a root post + JSONObject item = createSitePost("test", "test", Status.STATUS_OK); + String postName = item.getString("name"); + NodeRef postNodeRef = new NodeRef(item.getString("nodeRef")); + + // It doesn't have any replies yet + assertEquals(0, item.getInt("totalReplyCount")); + assertEquals(0, item.getInt("replyCount")); + + + // Add a reply + JSONObject reply = createReply(postNodeRef, "testR", "testR", Status.STATUS_OK); + NodeRef replyNodeRef = new NodeRef(reply.getString("nodeRef")); + String replyName = reply.getString("name"); + assertEquals("testR", reply.getString("title")); + assertEquals("testR", reply.getString("content")); + + // Fetch the reply and check + reply = getPost(replyNodeRef, Status.STATUS_OK); + assertEquals("testR", reply.getString("title")); + assertEquals("testR", reply.getString("content")); + + // Note - you can't fetch a reply by name, only by noderef + // It only works for primary posts as they share the topic name + getPost(replyName, Status.STATUS_NOT_FOUND); + + + // Check the main post, ensure the replies show up + item = getPost(postName, Status.STATUS_OK); + assertEquals(1, item.getInt("totalReplyCount")); + assertEquals(1, item.getInt("replyCount")); + + + // Delete the reply + deletePost(replyNodeRef, Status.STATUS_OK); + + // These nodes don't really get deleted at the moment + // Due to threading, we just add special marker text + // TODO Really we should probably delete posts with no attached replies + reply = getPost(replyNodeRef, Status.STATUS_OK); + assertEquals(DELETED_REPLY_POST_MARKER, reply.get("title")); + assertEquals(DELETED_REPLY_POST_MARKER, reply.get("content")); + + + // Fetch the top level post again, replies stay because they + // haven't really been deleted... + // TODO Really we should probably delete posts with no attached replies + item = getPost(postName, Status.STATUS_OK); + assertEquals(1, item.getInt("totalReplyCount")); + assertEquals(1, item.getInt("replyCount")); + } + + /** + * Test for the various listings: + * All, New, Hot (Most Active), Mine + */ + public void testListings() throws Exception + { + JSONObject result; + JSONObject item; + + + // Check all of the listings, none should have anything yet + result = getPosts(null, Status.STATUS_OK); + assertEquals(0, result.getInt("total")); + assertEquals(0, result.getInt("itemCount")); + assertEquals(0, result.getJSONArray("items").length()); + + result = getPosts("hot", Status.STATUS_OK); + assertEquals(0, result.getInt("total")); + assertEquals(0, result.getInt("itemCount")); + assertEquals(0, result.getJSONArray("items").length()); + + result = getPosts("mine", Status.STATUS_OK); + assertEquals(0, result.getInt("total")); + assertEquals(0, result.getInt("itemCount")); + assertEquals(0, result.getJSONArray("items").length()); + + result = getPosts("new?numdays=100", Status.STATUS_OK); + assertEquals(0, result.getInt("total")); + assertEquals(0, result.getInt("itemCount")); + assertEquals(0, result.getJSONArray("items").length()); + + + // Check with a noderef too + result = getPosts(FORUM_NODE, null, Status.STATUS_OK); + assertEquals(0, result.getInt("total")); + assertEquals(0, result.getInt("itemCount")); + assertEquals(0, result.getJSONArray("items").length()); + + result = getPosts(FORUM_NODE, "hot", Status.STATUS_OK); + assertEquals(0, result.getInt("total")); + assertEquals(0, result.getInt("itemCount")); + assertEquals(0, result.getJSONArray("items").length()); + + result = getPosts(FORUM_NODE, "mine", Status.STATUS_OK); + assertEquals(0, result.getInt("total")); + assertEquals(0, result.getInt("itemCount")); + assertEquals(0, result.getJSONArray("items").length()); + + result = getPosts(FORUM_NODE, "new?numdays=100", Status.STATUS_OK); + assertEquals(0, result.getInt("total")); + assertEquals(0, result.getInt("itemCount")); + assertEquals(0, result.getJSONArray("items").length()); + + + // Now add a few topics with replies + // Some of these will be created as different users + item = createSitePost("SiteTitle1", "Content", Status.STATUS_OK); + NodeRef siteTopic1 = new NodeRef(item.getString("nodeRef")); + this.authenticationComponent.setCurrentUser(USER_TWO); + item = createSitePost("SiteTitle2", "Content", Status.STATUS_OK); + NodeRef siteTopic2 = new NodeRef(item.getString("nodeRef")); + + item = createNodePost(FORUM_NODE, "NodeTitle1", "Content", Status.STATUS_OK); + NodeRef nodeTopic1 = new NodeRef(item.getString("nodeRef")); + this.authenticationComponent.setCurrentUser(USER_ONE); + item = createNodePost(FORUM_NODE, "NodeTitle2", "Content", Status.STATUS_OK); + NodeRef nodeTopic2 = new NodeRef(item.getString("nodeRef")); + item = createNodePost(FORUM_NODE, "NodeTitle3", "Content", Status.STATUS_OK); + NodeRef nodeTopic3 = new NodeRef(item.getString("nodeRef")); + + item = createReply(siteTopic1, "Reply1a", "Content", Status.STATUS_OK); + NodeRef siteReply1A = new NodeRef(item.getString("nodeRef")); + item = createReply(siteTopic1, "Reply1b", "Content", Status.STATUS_OK); + NodeRef siteReply1B = new NodeRef(item.getString("nodeRef")); + + this.authenticationComponent.setCurrentUser(USER_TWO); + item = createReply(siteTopic2, "Reply2a", "Content", Status.STATUS_OK); + NodeRef siteReply2A = new NodeRef(item.getString("nodeRef")); + item = createReply(siteTopic2, "Reply2b", "Content", Status.STATUS_OK); + NodeRef siteReply2B = new NodeRef(item.getString("nodeRef")); + item = createReply(siteTopic2, "Reply2c", "Content", Status.STATUS_OK); + NodeRef siteReply2C = new NodeRef(item.getString("nodeRef")); + + item = createReply(siteReply2A, "Reply2aa", "Content", Status.STATUS_OK); + NodeRef siteReply2AA = new NodeRef(item.getString("nodeRef")); + item = createReply(siteReply2A, "Reply2ab", "Content", Status.STATUS_OK); + NodeRef siteReply2AB = new NodeRef(item.getString("nodeRef")); + this.authenticationComponent.setCurrentUser(USER_ONE); + item = createReply(siteReply2AA, "Reply2aaa", "Content", Status.STATUS_OK); + NodeRef siteReply2AAA = new NodeRef(item.getString("nodeRef")); + + item = createReply(nodeTopic1, "ReplyN1a", "Content", Status.STATUS_OK); + NodeRef nodeReply1A = new NodeRef(item.getString("nodeRef")); + item = createReply(nodeReply1A, "ReplyN1aa", "Content", Status.STATUS_OK); + NodeRef nodeReply1AA = new NodeRef(item.getString("nodeRef")); + item = createReply(nodeReply1AA, "ReplyN1aaa", "Content", Status.STATUS_OK); + NodeRef nodeReply1AAA = new NodeRef(item.getString("nodeRef")); + + + // Check for totals + // We should get all the topics + result = getPosts(null, Status.STATUS_OK); + assertEquals(2, result.getInt("total")); + assertEquals(2, result.getInt("itemCount")); + assertEquals(2, result.getJSONArray("items").length()); + assertEquals("SiteTitle1", result.getJSONArray("items").getJSONObject(1).getString("title")); + assertEquals("SiteTitle2", result.getJSONArray("items").getJSONObject(0).getString("title")); + assertEquals(2, result.getJSONArray("items").getJSONObject(1).getInt("replyCount")); + assertEquals(3, result.getJSONArray("items").getJSONObject(0).getInt("replyCount")); + + result = getPosts(FORUM_NODE, null, Status.STATUS_OK); + assertEquals(3, result.getInt("total")); + assertEquals(3, result.getInt("itemCount")); + assertEquals(3, result.getJSONArray("items").length()); + assertEquals("NodeTitle1", result.getJSONArray("items").getJSONObject(2).getString("title")); + assertEquals("NodeTitle2", result.getJSONArray("items").getJSONObject(1).getString("title")); + assertEquals("NodeTitle3", result.getJSONArray("items").getJSONObject(0).getString("title")); + assertEquals(1, result.getJSONArray("items").getJSONObject(2).getInt("replyCount")); + assertEquals(0, result.getJSONArray("items").getJSONObject(1).getInt("replyCount")); + assertEquals(0, result.getJSONArray("items").getJSONObject(0).getInt("replyCount")); + + + // Check for "mine" + // User 1 has Site 1, and Nodes 2 + 3 + result = getPosts("mine", Status.STATUS_OK); + assertEquals(1, result.getInt("total")); + assertEquals(1, result.getInt("itemCount")); + assertEquals(1, result.getJSONArray("items").length()); + assertEquals("SiteTitle1", result.getJSONArray("items").getJSONObject(0).getString("title")); + assertEquals(2, result.getJSONArray("items").getJSONObject(0).getInt("replyCount")); + + result = getPosts(FORUM_NODE, "mine", Status.STATUS_OK); + assertEquals(2, result.getInt("total")); + assertEquals(2, result.getInt("itemCount")); + assertEquals(2, result.getJSONArray("items").length()); + assertEquals("NodeTitle2", result.getJSONArray("items").getJSONObject(0).getString("title")); + assertEquals("NodeTitle3", result.getJSONArray("items").getJSONObject(1).getString("title")); + assertEquals(0, result.getJSONArray("items").getJSONObject(0).getInt("replyCount")); + assertEquals(0, result.getJSONArray("items").getJSONObject(1).getInt("replyCount")); + + + // Check for recent (new) + // We should get all the topics, with the newest one first (rather than last as with others) + result = getPosts("new?numdays=2", Status.STATUS_OK); + assertEquals(2, result.getInt("total")); + assertEquals(2, result.getInt("itemCount")); + assertEquals(2, result.getJSONArray("items").length()); + assertEquals("SiteTitle2", result.getJSONArray("items").getJSONObject(0).getString("title")); + assertEquals("SiteTitle1", result.getJSONArray("items").getJSONObject(1).getString("title")); + assertEquals(3, result.getJSONArray("items").getJSONObject(0).getInt("replyCount")); + assertEquals(2, result.getJSONArray("items").getJSONObject(1).getInt("replyCount")); + + result = getPosts(FORUM_NODE, "new?numdays=2", Status.STATUS_OK); + assertEquals(3, result.getInt("total")); + assertEquals(3, result.getInt("itemCount")); + assertEquals(3, result.getJSONArray("items").length()); + assertEquals("NodeTitle3", result.getJSONArray("items").getJSONObject(0).getString("title")); + assertEquals("NodeTitle2", result.getJSONArray("items").getJSONObject(1).getString("title")); + assertEquals("NodeTitle1", result.getJSONArray("items").getJSONObject(2).getString("title")); + assertEquals(0, result.getJSONArray("items").getJSONObject(0).getInt("replyCount")); + assertEquals(0, result.getJSONArray("items").getJSONObject(1).getInt("replyCount")); + assertEquals(1, result.getJSONArray("items").getJSONObject(2).getInt("replyCount")); + + + // Check for hot + // Will only show topics with replies. Sorting is by replies, not date + result = getPosts("hot", Status.STATUS_OK); + assertEquals(2, result.getInt("total")); + assertEquals(2, result.getInt("itemCount")); + assertEquals(2, result.getJSONArray("items").length()); + assertEquals("SiteTitle2", result.getJSONArray("items").getJSONObject(0).getString("title")); + assertEquals("SiteTitle1", result.getJSONArray("items").getJSONObject(1).getString("title")); + assertEquals(3, result.getJSONArray("items").getJSONObject(0).getInt("replyCount")); + assertEquals(2, result.getJSONArray("items").getJSONObject(1).getInt("replyCount")); + + result = getPosts(FORUM_NODE, "hot", Status.STATUS_OK); + assertEquals(1, result.getInt("total")); + assertEquals(1, result.getInt("itemCount")); + assertEquals(1, result.getJSONArray("items").length()); + assertEquals("NodeTitle1", result.getJSONArray("items").getJSONObject(0).getString("title")); + assertEquals(1, result.getJSONArray("items").getJSONObject(0).getInt("replyCount")); + + + // Shift some of the posts into the past + // (Update the created and published dates) + pushCreatedDateBack(siteTopic1, 10); + pushCreatedDateBack(siteReply1B, -2); // Make it newer + + pushCreatedDateBack(nodeTopic2, 10); + pushCreatedDateBack(nodeTopic3, 4); + pushCreatedDateBack(nodeReply1AAA, -1); // Make it newer + + + // Re-check totals, only ordering changes + result = getPosts(null, Status.STATUS_OK); + assertEquals(2, result.getInt("total")); + assertEquals(2, result.getInt("itemCount")); + assertEquals(2, result.getJSONArray("items").length()); + assertEquals("SiteTitle1", result.getJSONArray("items").getJSONObject(1).getString("title")); + assertEquals("SiteTitle2", result.getJSONArray("items").getJSONObject(0).getString("title")); + assertEquals(2, result.getJSONArray("items").getJSONObject(1).getInt("replyCount")); + assertEquals(3, result.getJSONArray("items").getJSONObject(0).getInt("replyCount")); + + result = getPosts(FORUM_NODE, null, Status.STATUS_OK); + assertEquals(3, result.getInt("total")); + assertEquals(3, result.getInt("itemCount")); + assertEquals(3, result.getJSONArray("items").length()); + assertEquals("NodeTitle2", result.getJSONArray("items").getJSONObject(2).getString("title")); + assertEquals("NodeTitle3", result.getJSONArray("items").getJSONObject(1).getString("title")); + assertEquals("NodeTitle1", result.getJSONArray("items").getJSONObject(0).getString("title")); + assertEquals(0, result.getJSONArray("items").getJSONObject(2).getInt("replyCount")); + assertEquals(0, result.getJSONArray("items").getJSONObject(1).getInt("replyCount")); + assertEquals(1, result.getJSONArray("items").getJSONObject(0).getInt("replyCount")); + + + // Re-check recent, old ones vanish + result = getPosts("new?numdays=2", Status.STATUS_OK); + assertEquals(1, result.getInt("total")); + assertEquals(1, result.getInt("itemCount")); + assertEquals(1, result.getJSONArray("items").length()); + assertEquals("SiteTitle2", result.getJSONArray("items").getJSONObject(0).getString("title")); + assertEquals(3, result.getJSONArray("items").getJSONObject(0).getInt("replyCount")); + + result = getPosts(FORUM_NODE, "new?numdays=6", Status.STATUS_OK); + assertEquals(2, result.getInt("total")); + assertEquals(2, result.getInt("itemCount")); + assertEquals(2, result.getJSONArray("items").length()); + assertEquals("NodeTitle1", result.getJSONArray("items").getJSONObject(0).getString("title")); + assertEquals("NodeTitle3", result.getJSONArray("items").getJSONObject(1).getString("title")); + assertEquals(1, result.getJSONArray("items").getJSONObject(0).getInt("replyCount")); + assertEquals(0, result.getJSONArray("items").getJSONObject(1).getInt("replyCount")); + + result = getPosts(FORUM_NODE, "new?numdays=2", Status.STATUS_OK); + assertEquals(1, result.getInt("total")); + assertEquals(1, result.getInt("itemCount")); + assertEquals(1, result.getJSONArray("items").length()); + assertEquals("NodeTitle1", result.getJSONArray("items").getJSONObject(0).getString("title")); + assertEquals(1, result.getJSONArray("items").getJSONObject(0).getInt("replyCount")); + + + // Re-check "mine", no change except ordering + result = getPosts("mine", Status.STATUS_OK); + assertEquals(1, result.getInt("total")); + assertEquals(1, result.getInt("itemCount")); + assertEquals(1, result.getJSONArray("items").length()); + assertEquals("SiteTitle1", result.getJSONArray("items").getJSONObject(0).getString("title")); + assertEquals(2, result.getJSONArray("items").getJSONObject(0).getInt("replyCount")); + + result = getPosts(FORUM_NODE, "mine", Status.STATUS_OK); + assertEquals(2, result.getInt("total")); + assertEquals(2, result.getInt("itemCount")); + assertEquals(2, result.getJSONArray("items").length()); + assertEquals("NodeTitle2", result.getJSONArray("items").getJSONObject(0).getString("title")); + assertEquals("NodeTitle3", result.getJSONArray("items").getJSONObject(1).getString("title")); + assertEquals(0, result.getJSONArray("items").getJSONObject(0).getInt("replyCount")); + assertEquals(0, result.getJSONArray("items").getJSONObject(1).getInt("replyCount")); + + + // Re-check hot, some old ones vanish + result = getPosts("hot", Status.STATUS_OK); + assertEquals(2, result.getInt("total")); + assertEquals(2, result.getInt("itemCount")); + assertEquals(2, result.getJSONArray("items").length()); + assertEquals("SiteTitle2", result.getJSONArray("items").getJSONObject(0).getString("title")); + assertEquals("SiteTitle1", result.getJSONArray("items").getJSONObject(1).getString("title")); + assertEquals(3, result.getJSONArray("items").getJSONObject(0).getInt("replyCount")); + assertEquals(2, result.getJSONArray("items").getJSONObject(1).getInt("replyCount")); + + result = getPosts(FORUM_NODE, "hot", Status.STATUS_OK); + assertEquals(1, result.getInt("total")); + assertEquals(1, result.getInt("itemCount")); + assertEquals(1, result.getJSONArray("items").length()); + assertEquals("NodeTitle1", result.getJSONArray("items").getJSONObject(0).getString("title")); + assertEquals(1, result.getJSONArray("items").getJSONObject(0).getInt("replyCount")); + + + // Check paging + result = getPosts("limit", Status.STATUS_OK); + assertEquals(2, result.getInt("total")); + assertEquals(1, result.getInt("itemCount")); + assertEquals(1, result.getJSONArray("items").length()); + assertEquals("SiteTitle2", result.getJSONArray("items").getJSONObject(0).getString("title")); + assertEquals(3, result.getJSONArray("items").getJSONObject(0).getInt("replyCount")); + + result = getPosts(FORUM_NODE, "limit", Status.STATUS_OK); + assertEquals(3, result.getInt("total")); + assertEquals(1, result.getInt("itemCount")); + assertEquals(1, result.getJSONArray("items").length()); + assertEquals("NodeTitle1", result.getJSONArray("items").getJSONObject(0).getString("title")); + assertEquals(1, result.getJSONArray("items").getJSONObject(0).getInt("replyCount")); + } + + /** + * https://issues.alfresco.com/jira/browse/ALF-17443 reports that site contributors are unable + * to edit replies that they have made. + */ + public void testContributorCanEditReply() throws Exception + { + authenticationComponent.setCurrentUser(USER_ONE); + JSONObject post = createSitePost("Can contributors edit replies?", "The title says it all", Status.STATUS_OK); + NodeRef postNodeRef = new NodeRef(post.getString("nodeRef")); + + authenticationComponent.setCurrentUser(USER_TWO); + JSONObject reply = createReply(postNodeRef, "", "Let's see.", Status.STATUS_OK); + NodeRef replyNodeRef = new NodeRef(reply.getString("nodeRef")); + updateComment(replyNodeRef, "", "Yes I can", Status.STATUS_OK); + + authenticationComponent.setCurrentUser(USER_ONE); + + post = getPost(postNodeRef, Status.STATUS_OK); + assertEquals("Can contributors edit replies?", post.getString("title")); + assertEquals("The title says it all", post.getString("content")); + assertEquals(1, post.getInt("replyCount")); + + JSONObject replies = getReplies(postNodeRef, Status.STATUS_OK); + JSONArray items = replies.getJSONArray("items"); + assertEquals(1, items.length()); + + reply = items.getJSONObject(0); + assertEquals("Yes I can", reply.getString("content")); + + } + + /** + * Test for MNT-11964 + * @throws Exception + */ + public void testCreateForumPermission() throws Exception + { + this.authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName()); + + String siteName = SITE_SHORT_NAME_DISCUSSION + GUID.generate(); + this.siteService.createSite("ForumSitePreset", siteName, "SiteTitle", "SiteDescription", SiteVisibility.PUBLIC); + + String userName = USER_ONE + GUID.generate(); + createUser(userName, SiteModel.SITE_COLLABORATOR, siteName); + + // Check permissions for admin + checkForumPermissions(siteName); + + // Check permissions for user + this.authenticationComponent.setCurrentUser(userName); + checkForumPermissions(siteName); + + // Cleanup + this.authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName()); + this.siteService.deleteSite(siteName); + + // Create a new site as user + this.authenticationComponent.setCurrentUser(userName); + siteName = SITE_SHORT_NAME_DISCUSSION + GUID.generate(); + this.siteService.createSite("BlogSitePreset", siteName, "SiteTitle", "SiteDescription", SiteVisibility.PUBLIC); + + // Check permissions for user + checkForumPermissions(siteName); + + // Check permissions for admin + this.authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName()); + checkForumPermissions(siteName); + + // Cleanup + this.siteService.deleteSite(siteName); + this.personService.deletePerson(userName); + } + + private void checkForumPermissions(String siteName) throws Exception + { + String url = "/api/forum/site/" + siteName + "/" + COMPONENT_DISCUSSION + "/posts"; + Response response = sendRequest(new GetRequest(url), 200); + JSONObject result = new JSONObject(response.getContentAsString()); + + assertTrue("The user sould have permission to create a new discussion.", result.getJSONObject("forumPermissions").getBoolean("create")); + } +} diff --git a/src/test/java/org/alfresco/rest/api/tests/TestFavourites.java b/src/test/java/org/alfresco/rest/api/tests/TestFavourites.java index a2963cef5a..37854d0bdd 100644 --- a/src/test/java/org/alfresco/rest/api/tests/TestFavourites.java +++ b/src/test/java/org/alfresco/rest/api/tests/TestFavourites.java @@ -115,6 +115,8 @@ public class TestFavourites extends AbstractBaseApiTest private String person12Id; private TestPerson person14; private String person14Id; + private TestPerson person15; + private String person15Id; private TestNetwork network2; private TestPerson person21; @@ -176,6 +178,11 @@ public class TestFavourites extends AbstractBaseApiTest TestFavourites.this.person14 = network1.createUser(personInfo); assertNotNull(TestFavourites.this.person14); TestFavourites.this.person14Id = TestFavourites.this.person14.getId(); + name = GUID.generate(); + personInfo = new PersonInfo(name, name, name, "password", null, null, null, null, null, null, null); + TestFavourites.this.person15 = network1.createUser(personInfo); + assertNotNull(TestFavourites.this.person15); + TestFavourites.this.person15Id = TestFavourites.this.person15.getId(); TestFavourites.this.network2 = networksIt.next(); name = GUID.generate(); @@ -949,6 +956,22 @@ public class TestFavourites extends AbstractBaseApiTest assertEquals(HttpStatus.SC_METHOD_NOT_ALLOWED, e.getHttpResponse().getStatusCode()); } } + + // invalid orderBy param + { + publicApiClient.setRequestContext(new RequestContext(network1.getId(), person10Id)); + + try + { + Map params = new HashMap<>(); + params.put("orderBy", "invalid ASC"); + favouritesProxy.getFavourites(person10Id, createParams(null, params)); + } + catch(PublicApiException e) + { + assertEquals(HttpStatus.SC_BAD_REQUEST, e.getHttpResponse().getStatusCode()); + } + } } @Test @@ -1912,6 +1935,60 @@ public class TestFavourites extends AbstractBaseApiTest assertTrue(node1.getIsFavorite()); } + /** + * Test sort favourites using 'orderBy' parameter. + * + *

GET:

+ * {@literal :/alfresco/api//public/alfresco/versions/1/people//favorites?orderBy} + */ + @Test + public void testSortFavourites() throws Exception + { + setRequestContext(network1.getId(), person15Id, "password"); + + final NodeRef folderNodeRef1 = person1PublicFolders.get(0); // person1's folder (Test Folder1) + final NodeRef folderNodeRef2 = person1PublicFolders.get(1); // person1's folder (Test Folder2) + final NodeRef folderNodeRef3 = person1PublicFolders.get(2); // person1's folder (Test Folder3) + final NodeRef nodeRef1= person1PublicDocs.get(0); // a file (Test Doc1) + final NodeRef nodeRef2 = person1PublicDocs.get(1); // a file (Test Doc2) + + // Favourite the docs and folders + Favourite folder1Favourite = makeFolderFavourite(folderNodeRef1.getId()); + favouritesProxy.createFavourite(person15Id, folder1Favourite); + Favourite folder2Favourite = makeFolderFavourite(folderNodeRef2.getId()); + favouritesProxy.createFavourite(person15Id, folder2Favourite); + Favourite folder3Favourite = makeFolderFavourite(folderNodeRef3.getId()); + favouritesProxy.createFavourite(person15Id, folder3Favourite); + Favourite file1Favourite = makeFileFavourite(nodeRef1.getId()); + favouritesProxy.createFavourite(person15Id, file1Favourite); + Favourite file2Favourite = makeFileFavourite(nodeRef2.getId()); + favouritesProxy.createFavourite(person15Id, file2Favourite); + + // Order by title ASC + Map params = new HashMap<>(); + params.put("orderBy", "title ASC"); + + List favourites = favouritesProxy.getFavourites(person15Id, createParams(null,params)).getList(); + assertTrue(favourites.size() == 5); + assertTrue(favourites.get(0).getTargetGuid().equals(nodeRef1.getId())); + assertTrue(favourites.get(1).getTargetGuid().equals(nodeRef2.getId())); + assertTrue(favourites.get(2).getTargetGuid().equals(folderNodeRef1.getId())); + assertTrue(favourites.get(3).getTargetGuid().equals(folderNodeRef2.getId())); + assertTrue(favourites.get(4).getTargetGuid().equals(folderNodeRef3.getId())); + + // Order by type ASC, title DESC + params = new HashMap<>(); + params.put("orderBy", "type DESC, title DESC"); + + favourites = favouritesProxy.getFavourites(person15Id, createParams(null,params)).getList(); + assertTrue(favourites.size() == 5); + assertTrue(favourites.get(0).getTargetGuid().equals(folderNodeRef3.getId())); + assertTrue(favourites.get(1).getTargetGuid().equals(folderNodeRef2.getId())); + assertTrue(favourites.get(2).getTargetGuid().equals(folderNodeRef1.getId())); + assertTrue(favourites.get(3).getTargetGuid().equals(nodeRef2.getId())); + assertTrue(favourites.get(4).getTargetGuid().equals(nodeRef1.getId())); + } + private void assertPathInfo(PathInfo expectedPathInfo, String expectedPathName, boolean expectedIsComplete) { assertNotNull("Path info was requested.", expectedPathInfo);