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="activityService" ref="activityService"/>
<property name="discussionService" ref="DiscussionService"/>
<property name="permissionService" ref="PermissionService" />
</bean>
<!-- Fetches the details of one discussions post or topic -->
<!--
<bean id="webscript.org.alfresco.repository.discussions.posts.forum-post.get"
class="org.alfresco.repo.web.scripts.discussion.ForumPostGet"
parent="abstractDiscussionWebScript">
</bean>
-->
<!-- Deletes a discussion post or topic -->
<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 org.alfresco.query.PagingRequest;
import org.alfresco.query.PagingResults;
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.discussion.DiscussionService;
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.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.site.SiteInfo;
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);
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)
{
@@ -78,11 +90,6 @@ public abstract class AbstractDiscussionWebScript extends DeclarativeWebScript
this.siteService = siteService;
}
public void setDiscussionService(DiscussionService discussionService)
{
this.discussionService = discussionService;
}
public void setPersonService(PersonService personService)
{
this.personService = personService;
@@ -93,6 +100,16 @@ public abstract class AbstractDiscussionWebScript extends DeclarativeWebScript
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
{
@@ -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)
{
if(username == null || username.length() == 0)
@@ -220,36 +271,105 @@ public abstract class AbstractDiscussionWebScript extends DeclarativeWebScript
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>();
res.put("topic", topic);
res.put("node", topic.getNodeRef());
res.put("name", topic.getSystemName());
res.put("title", topic.getTitle());
res.put("tags", topic.getTags());
Map<String, Object> item = new HashMap<String, Object>();
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<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
res.put("createdOn", topic.getCreatedAt());
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())
// Ensure we got a primary post
if(posts.getPage().size() == 0)
{
if(res.get(key) == null)
{
res.put(key, "");
}
throw new WebScriptException(Status.STATUS_PRECONDITION_FAILED,
"First (primary) post was missing from the topic, can't fetch");
}
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
@@ -310,6 +430,13 @@ public abstract class AbstractDiscussionWebScript extends DeclarativeWebScript
{
String name = templateVars.get("path");
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") &&

View File

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