ALF-9153 Convert the single topic/post fetch webscript to be Java backed and Lucene Free

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@29765 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Nick Burch
2011-08-15 14:50:38 +00:00
parent 98eedd086c
commit 8acb571ac5
5 changed files with 229 additions and 83 deletions

View File

@@ -1,41 +0,0 @@
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/requestutils.lib.js">
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/discussions/topicpost.lib.js">
/*
* Fetches the correct post data (either topic post or post depending on the node type
*/
function fetchPostData(node)
{
// we have to differentiate here whether this is a top-level post or a reply
// only in the first case we fetch all information (as returned by forum/.../posts)
if (node.type == "{http://www.alfresco.org/model/forum/1.0}topic")
{
model.postData = getTopicPostData(node);
}
else if (node.type == "{http://www.alfresco.org/model/forum/1.0}post")
{
model.postData = getReplyPostData(node);
}
else
{
status.setCode(STATUS_BAD_REQUEST, "Incompatible node type. Required either fm:topic or fm:post. Received: " + node.type);
}
}
function main()
{
// get requested node
var node = getRequestNode();
if (status.getCode() != status.STATUS_OK)
{
return;
}
fetchPostData(node);
// fetch the contentLength param
var contentLength = args["contentLength"] != undefined ? parseInt(args["contentLength"]) : -1;
model.contentLength = isNaN(contentLength) ? -1 : contentLength;
}
main();

View File

@@ -1568,15 +1568,14 @@
<property name="personService" ref="PersonService"/> <property name="personService" ref="PersonService"/>
<property name="activityService" ref="activityService"/> <property name="activityService" ref="activityService"/>
<property name="discussionService" ref="DiscussionService"/> <property name="discussionService" ref="DiscussionService"/>
<property name="permissionService" ref="PermissionService" />
</bean> </bean>
<!-- Fetches the details of one discussions post or topic --> <!-- Fetches the details of one discussions post or topic -->
<!--
<bean id="webscript.org.alfresco.repository.discussions.posts.forum-post.get" <bean id="webscript.org.alfresco.repository.discussions.posts.forum-post.get"
class="org.alfresco.repo.web.scripts.discussion.ForumPostGet" class="org.alfresco.repo.web.scripts.discussion.ForumPostGet"
parent="abstractDiscussionWebScript"> parent="abstractDiscussionWebScript">
</bean> </bean>
-->
<!-- Deletes a discussion post or topic --> <!-- Deletes a discussion post or topic -->
<bean id="webscript.org.alfresco.repository.discussions.posts.forum-post.delete" <bean id="webscript.org.alfresco.repository.discussions.posts.forum-post.delete"

View File

@@ -23,7 +23,10 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.alfresco.query.PagingRequest; import org.alfresco.query.PagingRequest;
import org.alfresco.query.PagingResults;
import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.MimetypeMap;
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.activities.ActivityService;
import org.alfresco.service.cmr.discussion.DiscussionService; import org.alfresco.service.cmr.discussion.DiscussionService;
import org.alfresco.service.cmr.discussion.PostInfo; import org.alfresco.service.cmr.discussion.PostInfo;
@@ -31,6 +34,8 @@ import org.alfresco.service.cmr.discussion.TopicInfo;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.site.SiteInfo; import org.alfresco.service.cmr.site.SiteInfo;
import org.alfresco.service.cmr.site.SiteService; import org.alfresco.service.cmr.site.SiteService;
@@ -61,12 +66,19 @@ public abstract class AbstractDiscussionWebScript extends DeclarativeWebScript
private static Log logger = LogFactory.getLog(AbstractDiscussionWebScript.class); private static Log logger = LogFactory.getLog(AbstractDiscussionWebScript.class);
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 // Injected services
protected NodeService nodeService; protected NodeService nodeService;
protected SiteService siteService; protected SiteService siteService;
protected PersonService personService; protected PersonService personService;
protected ActivityService activityService; protected ActivityService activityService;
protected DiscussionService discussionService; protected DiscussionService discussionService;
protected PermissionService permissionService;
public void setNodeService(NodeService nodeService) public void setNodeService(NodeService nodeService)
{ {
@@ -78,11 +90,6 @@ public abstract class AbstractDiscussionWebScript extends DeclarativeWebScript
this.siteService = siteService; this.siteService = siteService;
} }
public void setDiscussionService(DiscussionService discussionService)
{
this.discussionService = discussionService;
}
public void setPersonService(PersonService personService) public void setPersonService(PersonService personService)
{ {
this.personService = personService; this.personService = personService;
@@ -93,6 +100,16 @@ public abstract class AbstractDiscussionWebScript extends DeclarativeWebScript
this.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) throws JSONException protected String getOrNull(JSONObject json, String key) throws JSONException
{ {
@@ -206,7 +223,41 @@ public abstract class AbstractDiscussionWebScript extends DeclarativeWebScript
} }
} }
// TODO Is this needed? /**
* 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) protected Object buildPerson(String username)
{ {
if(username == null || username.length() == 0) if(username == null || username.length() == 0)
@@ -220,36 +271,105 @@ public abstract class AbstractDiscussionWebScript extends DeclarativeWebScript
return person; return person;
} }
// TODO Match JS /*
protected Map<String, Object> renderTopic(TopicInfo topic) * Was topicpost.lib.js getReplyPostData
*
* TODO Switch the FTL to prefer the Info object rather than the ScriptNode
*/
protected Map<String, Object> renderPost(PostInfo post, SiteInfo site)
{ {
Map<String, Object> res = new HashMap<String, Object>(); Map<String, Object> item = new HashMap<String, Object>();
res.put("topic", topic); item.put(KEY_IS_TOPIC_POST, false);
res.put("node", topic.getNodeRef()); item.put(KEY_POST, post.getNodeRef());
res.put("name", topic.getSystemName()); item.put(KEY_CAN_EDIT, canUserEditPost(post, site));
res.put("title", topic.getTitle()); item.put(KEY_AUTHOR, buildPerson(post.getCreator()));
res.put("tags", topic.getTags()); return item;
}
/*
* Was topicpost.lib.js getTopicPostData / getTopicPostDataFromTopicAndPosts
*
* TODO Switch the FTL to prefer the Info object rather than the ScriptNode
*/
protected Map<String, Object> renderTopic(TopicInfo topic, SiteInfo site)
{
// Fetch all the posts for the topic
// TODO We actually only need the count, the first and the last
// In the interest of keeping our life simply, get all of them
PagingRequest paging = new PagingRequest(MAX_QUERY_ENTRY_COUNT);
paging.setRequestTotalCountMax(MAX_QUERY_ENTRY_COUNT);
PagingResults<PostInfo> posts = discussionService.listPosts(topic, paging);
// Both forms used for dates // Ensure we got a primary post
res.put("createdOn", topic.getCreatedAt()); if(posts.getPage().size() == 0)
res.put("modifiedOn", topic.getModifiedAt());
res.put("created", topic.getCreatedAt());
res.put("modified", topic.getModifiedAt());
// FTL needs a script node of the people
res.put("createdBy", buildPerson(topic.getCreator()));
res.put("modifiedBY", buildPerson(topic.getModifier()));
// We want blank instead of null
for(String key : res.keySet())
{ {
if(res.get(key) == null) throw new WebScriptException(Status.STATUS_PRECONDITION_FAILED,
{ "First (primary) post was missing from the topic, can't fetch");
res.put(key, "");
}
} }
return res; // Grab the posts of interest
PostInfo primaryPost = posts.getPage().get(0);
// The posts in a topic listing are ordered by created date
PostInfo mostRecentPost = posts.getPage().get(posts.getPage().size()-1);
// Build the details
Map<String, Object> item = new HashMap<String, Object>();
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", posts.getTotalResultCount().getFirst() - 1);
// We want details on the most recent post
if(posts.getPage().size() > 1)
{
item.put("lastReply", mostRecentPost.getNodeRef());
item.put("lastReplyBy", buildPerson(mostRecentPost.getCreator()));
}
// Include the tags
item.put("tags", topic.getTags());
// All done
return item;
}
protected Map<String, Object> buildCommonModel(SiteInfo site, TopicInfo topic,
PostInfo post, WebScriptRequest req)
{
// Build the common model parts
Map<String, Object> model = new HashMap<String, Object>();
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 @Override
@@ -310,6 +430,13 @@ public abstract class AbstractDiscussionWebScript extends DeclarativeWebScript
{ {
String name = templateVars.get("path"); String name = templateVars.get("path");
topic = discussionService.getTopic(site.getShortName(), name); topic = discussionService.getTopic(site.getShortName(), name);
if(topic == null)
{
String error = "Could not find topic: " + name;
throw new WebScriptException(Status.STATUS_NOT_FOUND, error);
}
nodeRef = topic.getNodeRef();
} }
} }
else if(templateVars.containsKey("store_type") && else if(templateVars.containsKey("store_type") &&

View File

@@ -18,7 +18,6 @@
*/ */
package org.alfresco.repo.web.scripts.discussion; package org.alfresco.repo.web.scripts.discussion;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.alfresco.service.cmr.discussion.PostInfo; import org.alfresco.service.cmr.discussion.PostInfo;
@@ -47,14 +46,7 @@ public class ForumPostDelete extends AbstractDiscussionWebScript
Status status, Cache cache) Status status, Cache cache)
{ {
// Build the common model parts // Build the common model parts
Map<String, Object> model = new HashMap<String, Object>(); Map<String, Object> model = buildCommonModel(site, topic, post, req);
model.put("topic", topic);
model.put("post", post);
if(site != null)
{
model.put("siteId", site.getShortName());
model.put("site", site);
}
// Are we deleting a topic, or a post in it? // Are we deleting a topic, or a post in it?

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.web.scripts.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.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
{
private static final String KEY_POSTDATA = "postData";
@Override
protected Map<String, Object> executeImpl(SiteInfo site, NodeRef nodeRef,
TopicInfo topic, PostInfo post, WebScriptRequest req, JSONObject json,
Status status, Cache cache)
{
// Build the common model parts
Map<String, Object> 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;
}
}