- added paged results lib methods

- cleanup for blogs, comments and discussions API
- deleting a forum reply post now changes the content to "removed" instead of deleting the node
- fix for comment deletion and archive filter in client side javascript

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@9612 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Michael Ru
2008-07-01 13:44:33 +00:00
parent 0c4683657d
commit 310d127f36
32 changed files with 984 additions and 1134 deletions

View File

@@ -1,88 +1,49 @@
/** Name of the blog details aspect. */
const BLOG_DETAILS_ASPECT = "blg:blogDetails"; const BLOG_DETAILS_ASPECT = "blg:blogDetails";
/** /**
* Fetches the blog properties from the json object and adds them to the array. * Fetches the blog properties from the json object and adds them to an array
* using the correct property names as indexes.
*/ */
function getBlogPropertiesArray() function getBlogPropertiesArray()
{ {
var arr = new Array(); var arr = new Array();
if (json.has("blogType")) if (json.has("blogType"))
{ {
arr["blg:blogImplementation"] = json.get("blogType"); arr["blg:blogImplementation"] = json.get("blogType");
} }
if (json.has("blogId")) if (json.has("blogId"))
{ {
arr["blg:id"] = json.get("blogId"); arr["blg:id"] = json.get("blogId");
} }
if (json.has("blogName")) if (json.has("blogName"))
{ {
arr["blg:name"] = json.get("blogName"); arr["blg:name"] = json.get("blogName");
} }
if (json.has("blogDescription")) if (json.has("blogDescription"))
{ {
arr["blg:description"] = json.get("blogDescription"); arr["blg:description"] = json.get("blogDescription");
} }
if (json.has("blogUrl")) if (json.has("blogUrl"))
{ {
arr["blg:url"] = json.get("blogUrl"); arr["blg:url"] = json.get("blogUrl");
} }
if (json.has("username")) if (json.has("username"))
{ {
arr["blg:userName"] = json.get("username"); arr["blg:userName"] = json.get("username");
} }
if (json.has("password")) if (json.has("password"))
{ {
arr["blg:password"] = json.get("password"); arr["blg:password"] = json.get("password");
} }
return arr; return arr;
} }
/** /**
* Returns the data of a blog post. * Returns the data object of a blog node.
*/ */
function getBlogData(node) function getBlogData(node)
{ {
return node; return node;
} }
/**
* Returns an array containing all topics found in the passed array.
* Filters out non-fm:topic nodes.
*/
function getBlogListData(nodes, index, count)
{
var items = new Array();
var i;
var added = 0;
for (i = index; i < nodes.length && added < count; i++)
{
items.push(getBlogData(nodes[i]));
added++;
}
return ({
"total" : nodes.length,
"pageSize" : count,
"startIndex" : index,
"itemCount" : items.length,
"items": items
});
}
/**
* Returns a list of topics, as returned by the lucene query
*/
function getBlogsListByLuceneQuery(node, luceneQuery, sortAttribute, ascending, index, count)
{
var nodes = null;
if (sortAttribute != null)
{
nodes = search.luceneSearch(node.nodeRef.storeRef.toString(), luceneQuery, sortAttribute, ascending);
}
else
{
nodes = search.luceneSearch(luceneQuery);
}
return getBlogListData(nodes, index, count);
}

View File

@@ -2,16 +2,14 @@
function main() function main()
{ {
// get requested node // get requested node
var node = getRequestNode(); var node = getRequestNode();
if (status.getCode() != status.STATUS_OK) if (status.getCode() != status.STATUS_OK)
{ {
return; return;
} }
model.item = node; model.item = getBlogData(node);
// process additional parameters
} }
main(); main();

View File

@@ -6,38 +6,38 @@
*/ */
function updateBlog(node) function updateBlog(node)
{ {
var arr = getBlogPropertiesArray(); var arr = getBlogPropertiesArray();
// check whether we already have the aspect added to the node. // check whether we already have the aspect added to the node.
if (node.hasAspect(BLOG_DETAILS_ASPECT)) if (node.hasAspect(BLOG_DETAILS_ASPECT))
{ {
for (propName in arr) for (propName in arr)
{ {
node.properties[propName] = arr[propName]; node.properties[propName] = arr[propName];
} }
} }
else else
{ {
// if not, add the aspect on the fly // if not, add the aspect on the fly
node.addAspect(BLOG_DETAILS_ASPECT, arr); node.addAspect(BLOG_DETAILS_ASPECT, arr);
} }
node.save(); node.save();
} }
function main() function main()
{ {
// get requested node // get requested node
var node = getRequestNode(); var node = getRequestNode();
if (status.getCode() != status.STATUS_OK) if (status.getCode() != status.STATUS_OK)
{ {
return; return;
} }
// update // update blog
updateBlog(node); updateBlog(node);
model.item = node; model.item = getBlogData(node);
} }
main(); main();

View File

@@ -1,34 +1,4 @@
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/comments/comments.lib.js">
const DRAFT_FOLDER_NAME = "Drafts";
/**
* Returns the draft folder.
*/
function getOrCreateDraftsFolder(node)
{
var draftFolder = node.childByNamePath(DRAFT_FOLDER_NAME);
if (draftFolder == null)
{
draftFolder = node.createNode(DRAFT_FOLDER_NAME, "cm:folder");
}
return draftFolder;
}
function getCommentsCount(node)
{
// check whether there are comments
// PENDING: this should use the comments API
if (node.hasAspect("fm:discussable"))
{
var forumNode = node.childAssocs["fm:discussion"][0];
var topicNode = forumNode.childAssocs["cm:contains"][0];
return topicNode.childAssocs["cm:contains"].length
}
else
{
return 0;
}
}
/** /**
* Returns the data of a blog post. * Returns the data of a blog post.
@@ -48,69 +18,19 @@ function getBlogPostData(node)
// outOfDate // outOfDate
if ((node.properties["blg:lastUpdate"] != undefined)) if ((node.properties["blg:lastUpdate"] != undefined))
{ {
if ((node.properties["cm:modified"] - node.properties["blg:lastUpdate"]) > 5000) if ((node.properties["cm:modified"] - node.properties["blg:lastUpdate"]) > 5000)
{ {
data.outOfDate = true; data.outOfDate = true;
} }
else else
{ {
data.outOfDate = false; data.outOfDate = false;
} }
} }
else else
{ {
data.outOfDate = false; data.outOfDate = false;
} }
return data; return data;
} }
/**
* Returns the data of a blog post.
*/
/*function getBlogPostData(node)
{
return node;
}*/
/**
* Returns an array containing all topics found in the passed array.
* Filters out non-fm:topic nodes.
*/
function getBlogPostListData(nodes, index, count)
{
var items = new Array();
var i;
var added = 0;
for (i = index; i < nodes.length && added < count; i++)
{
items.push(getBlogPostData(nodes[i]));
added++;
}
return ({
"total" : nodes.length,
"pageSize" : count,
"startIndex" : index,
"itemCount" : items.length,
"items": items
});
}
/**
* Returns a list of topics, as returned by the lucene query
*/
function getBlogPostListByLuceneQuery(node, luceneQuery, sortAttribute, ascending, index, count)
{
var nodes = null;
if (sortAttribute != null)
{
nodes = search.luceneSearch(node.nodeRef.storeRef.toString(), luceneQuery, sortAttribute, ascending);
}
else
{
nodes = search.luceneSearch(luceneQuery);
}
return getBlogPostListData(nodes, index, count);
}

View File

@@ -1,65 +1,84 @@
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/requestutils.lib.js"> <import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/requestutils.lib.js">
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/blogs/blogpost.lib.js"> <import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/blogs/blogpost.lib.js">
var POST_ACTION = "publish"; const POST_ACTION = "publish";
var UPDATE_ACTION = "update"; const UPDATE_ACTION = "update";
var REMOVE_ACTION = "unpublish"; const REMOVE_ACTION = "unpublish";
function executeAction(node, action) /**
* Validates the action to execute.
* @return the action name to be used for the blog-post action or null if the specified action is invalid
*/
function validateAction(node, action)
{ {
var blogAction = ""; var blogAction = null;
var isPublished = false; var isPublished = false;
if ((node.hasAspect("blg:blogPost")) && (node.properties["blg:published"] == true)) if ((node.hasAspect("blg:blogPost")) && (node.properties["blg:published"] == true))
{ {
isPublished = true; isPublished = true;
} }
// make sure we have a real JavaScript object, otherwise switch won't work correctly
// make sure we have a real JavaScript object,
// otherwise switch won't work correctly
action = "" + action; action = "" + action;
switch (action) switch (action)
{ {
case POST_ACTION: case POST_ACTION:
blogAction = (isPublished ? "" : "post"); blogAction = (isPublished ? "" : "post");
break; break;
case UPDATE_ACTION: case UPDATE_ACTION:
blogAction = (isPublished ? "update" : ""); blogAction = (isPublished ? "update" : "");
break; break;
case REMOVE_ACTION: case REMOVE_ACTION:
blogAction = (isPublished ? "remove" : ""); blogAction = (isPublished ? "remove" : "");
break; break;
} }
if (blogAction != "") if (blogAction === null)
{
// set an error status
status.setCode(status.STATUS_BAD_REQUEST, "Invalid action specified (node in wrong state?)");
return null;
}
else
{
return blogAction;
}
}
/**
* Publishe, update or removes the blog from/to the external blog
*/
function executeAction(node, action)
{
// get the blog action to call (the names differ from the constants defined above)
var blogAction = validateAction(node, action);
if (blogAction != null)
{ {
var blog = actions.create("blog-post"); var blog = actions.create("blog-post");
blog.parameters.action = blogAction; blog.parameters.action = blogAction;
blog.execute(node); blog.execute(node);
result = blog.parameters["result"];
logger.log("Blog action result: " + result); // PENDING: how do we know that the action succeeded?
model.message = result; model.result = blog.parameters["result"];
/* Check whether action succeeded logger.log("Blog action result: " + result);
if (result != "sfsdf") }
{
}*/
}
} }
function main() function main()
{ {
// get requested node // get requested node
var node = getRequestNode(); var node = getRequestNode();
if (status.getCode() != status.STATUS_OK) if (status.getCode() != status.STATUS_OK)
{ {
return; return;
} }
// fetch and execute the action // fetch and execute the action
var action = json.get("action"); var action = json.get("action");
executeAction(node, action); executeAction(node, action);
model.item = getBlogPostData(node);
// get the updated data for the blog post
model.item = getBlogPostData(node);
} }
main(); main();

View File

@@ -1,33 +1,31 @@
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/requestutils.lib.js"> <import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/requestutils.lib.js">
/** /**
* Deletes a topic node. * Deletes a blog post node.
*/ */
function deletePost(postNode) function deleteBlogPost(postNode)
{ {
// we simply delete the topic // delete the node
var qnamePath = postNode.qnamePath; var nodeRef = postNode.nodeRef;
logger.log("Deleting node " + qnamePath); var isDeleted = postNode.remove();
var isDeleted = postNode.remove(); if (! isDeleted)
logger.log("Node deleted: " + isDeleted); {
if (! isDeleted) status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, "Unable to delete node: " + nodeRef);
{ return;
status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, "Unable to delete node: " + qnamePath); }
return; model.message = "Node " + nodeRef + " deleted";
}
model.message = "Node " + qnamePath + " deleted";
} }
function main() function main()
{ {
// get requested node // get requested node
var node = getRequestNode(); var node = getRequestNode();
if (status.getCode() != status.STATUS_OK) if (status.getCode() != status.STATUS_OK)
{ {
return; return;
} }
deletePost(node); deleteBlogPost(node);
} }
main(); main();

View File

@@ -3,19 +3,16 @@
function main() function main()
{ {
// get requested node // get requested node
var node = getRequestNode(); var node = getRequestNode();
if (status.getCode() != status.STATUS_OK) if (status.getCode() != status.STATUS_OK)
{ {
return; return;
} }
// PENDING: type check? // assign data
model.item = getBlogPostData(node);
// assign data model.contentFormat = (args["contentFormat"] != undefined) ? args["contentFormat"] : "full";
model.item = getBlogPostData(node);
model.contentFormat = (args["contentFormat"] != undefined) ? args["contentFormat"] : "full";
} }
main(); main();

View File

@@ -2,39 +2,30 @@
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/blogs/blogpost.lib.js"> <import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/blogs/blogpost.lib.js">
/** /**
* Creates a post inside the passed forum node. * Updates a blog post node
*/ */
function updatePost(postNode) function updateBlogPost(postNode)
{ {
/*var name = ""; // fetch the new data
if (json.has("name")) var title = "";
{ if (json.has("title"))
title = json.get("name"); {
}*/ title = json.get("title");
var title = ""; }
if (json.has("title")) var content = json.get("content");
{
title = json.get("title");
}
var content = json.get("content");
// update the topic title // update the node
postNode.properties.title = title; postNode.properties.title = title;
postNode.mimetype = "text/html"; postNode.mimetype = "text/html";
postNode.content = content; postNode.content = content;
postNode.save(); postNode.save();
// PENDING:
// check whether it is draft mode // check whether it is draft mode
/*if (postNode.hasAspect("cm:workingcopy") && json.get("draft") == "false") /*if (postNode.hasAspect("cm:workingcopy") && json.get("draft") == "false")
{ {
postNode.removeAspect("cm:workingcopy"); postNode.removeAspect("cm:workingcopy");
}*/ }*/
// try to change the file name
/*if (name.length > 0)
{
postNode.name = name;
}*/
} }
function main() function main()
@@ -46,8 +37,8 @@ function main()
return; return;
} }
// update // update blog post
updatePost(node); updateBlogPost(node);
model.item = getBlogPostData(node); model.item = getBlogPostData(node);
} }

View File

@@ -1,91 +1,103 @@
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/requestutils.lib.js"> <import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/requestutils.lib.js">
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/blogs/blogpost.lib.js"> <import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/blogs/blogpost.lib.js">
/**
* Returns the date representing the begin of a month (the first day at 00:00:00)
*/
function getBeginOfMonthDate(date) function getBeginOfMonthDate(date)
{ {
return new Date(date.getFullYear(), date.getMonth(), 1); return new Date(date.getFullYear(), date.getMonth(), 1);
}
function getEndOfMonthDate(date)
{
var year = date.getFullYear();
var month = date.getMonth();
var beginOfNextMonth = new Date(year, month + 1, 1); // will increment year by 1 if month > 11
return new Date(beginOfNextMonth.getTime() - 1);
} }
/** /**
* Creates an object containing information about the month. * Returns the date representing the last second of a month (23:59:59)
* This object holds all the data returned. */
function getEndOfMonthDate(date)
{
var year = date.getFullYear();
var month = date.getMonth();
var beginOfNextMonth = new Date(year, month + 1, 1); // will increment year if month > 11
return new Date(beginOfNextMonth.getTime() - 1); // one less to get the last millisecond of the previous day
}
/**
* Create an object containing information about the month specified by date.
*/ */
function getMonthDataObject(date) function getMonthDataObject(date)
{ {
var data = {}; var data = {};
data.year = date.getFullYear(); data.year = date.getFullYear();
data.month = date.getMonth(); data.month = date.getMonth();
data.firstPostInMonth = date; data.firstPostInMonth = date;
data.beginOfMonth = getBeginOfMonthDate(date); data.beginOfMonth = getBeginOfMonthDate(date);
data.beginOfMonthMillis = data.beginOfMonth.getTime(); data.endOfMonth = getEndOfMonthDate(date);
data.endOfMonth = getEndOfMonthDate(date); data.count = 1;
data.endOfMonthMillis = data.endOfMonth.getTime(); return data;
data.count = 1;
return data;
} }
/** /**
* Fetches all posts found in the forum. * Fetches data for each month for which posts exist, plus the count of each.
* Note: If no posts could be found, this method will return the current month
* but with a count of posts equals zero.
*/ */
function getBlogPostMonths(node) function getBlogPostMonths(node)
{ {
// query information // query information
var luceneQuery = " +TYPE:\"{http://www.alfresco.org/model/content/1.0}content\"" + var luceneQuery = " +TYPE:\"{http://www.alfresco.org/model/content/1.0}content\"" +
" +PATH:\"" + node.qnamePath + "/*\" "; " +PATH:\"" + node.qnamePath + "/*\" ";
var sortAttribute = "@{http://www.alfresco.org/model/content/1.0}created"; var sortAttribute = "@{http://www.alfresco.org/model/content/1.0}created";
nodes = search.luceneSearch(node.nodeRef.storeRef.toString(), luceneQuery, sortAttribute, true); nodes = search.luceneSearch(node.nodeRef.storeRef.toString(), luceneQuery, sortAttribute, true);
// fetch all dates with different month and year. Throw away doubles. // will hold the months information
var data = new Array(); var data = new Array();
if (nodes.length > 0) { // do we have posts?
var curr = nodes[0].properties["cm:created"]; if (nodes.length > 0)
var currData = getMonthDataObject(curr); {
data.push(currData); var currYear = -1;
var currMonth = -1;
for (var x=1; x < nodes.length; x++) var currData = null;
{ for (var x=0; x < nodes.length; x++)
var date = nodes[x].properties["cm:created"]; {
// check whether we are in a new month var date = nodes[x].properties["cm:created"];
if (curr.getFullYear() != date.getFullYear() || curr.getMonth() != date.getMonth())
{ // is this a new month?
curr = node; if (currYear != date.getFullYear() || currMonth != date.getMonth())
currData = getMonthDataObject(curr); {
data.push(currData); currYear = date.getFullYear();
} currMonth = date.getMonth();
// or still the same one currData = getMonthDataObject(date);
else data.push(currData);
{ }
currData.count += 1; // otherwise just increment the counter
} else
} {
} currData.count += 1;
}
return data; }
}
// if not, add the current month with count = 0
else
{
var emptyData = getMonthdataObject(new Date());
emptyData.count = 0;
data.push(emptyData);
}
return data;
} }
function main() function main()
{ {
// get requested node // get requested node
var node = getRequestNode(); var node = getRequestNode();
if (status.getCode() != status.STATUS_OK) if (status.getCode() != status.STATUS_OK)
{ {
return; return;
} }
// fetch the months // fetch the months
model.data = getBlogPostMonths(node); model.data = getBlogPostMonths(node);
} }
main(); main();

View File

@@ -1,63 +1,64 @@
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/requestutils.lib.js"> <import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/requestutils.lib.js">
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/searchutils.lib.js"> <import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/searchutils.lib.js">
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/generic-paged-results.lib.js">
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/blogs/blogpost.lib.js"> <import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/blogs/blogpost.lib.js">
/** /**
* Fetches all posts found in the forum. * Fetches all posts of the given blog
*/ */
function getBlogPostList(node, fromDate, toDate, index, count) function getBlogPostList(node, fromDate, toDate, index, count)
{ {
// query information // query information
var luceneQuery = " +TYPE:\"{http://www.alfresco.org/model/content/1.0}content\"" + var luceneQuery = " +TYPE:\"{http://www.alfresco.org/model/content/1.0}content\"" +
" +PATH:\"" + node.qnamePath + "/*\" "; " +PATH:\"" + node.qnamePath + "/*\" ";
// date query ? // date query ?
if (fromDate != null || toDate != null) if (fromDate != null || toDate != null)
{ {
luceneQuery += getCreationDateRangeQuery(fromDate, toDate); luceneQuery += getCreationDateRangeQuery(fromDate, toDate);
} }
var sortAttribute = "@{http://www.alfresco.org/model/content/1.0}created"; var sortAttribute = "@{http://www.alfresco.org/model/content/1.0}created";
// get the data // get the data
return getBlogPostListByLuceneQuery(node, luceneQuery, sortAttribute, false, index, count); return getPagedResultsDataByLuceneQuery(node, luceneQuery, sortAttribute, false, index, count, getBlogPostData);
} }
function main() function main()
{ {
// get requested node // get requested node
var node = getRequestNode(); var node = getRequestNode();
if (status.getCode() != status.STATUS_OK) if (status.getCode() != status.STATUS_OK)
{ {
return; return;
} }
// process additional parameters // process additional parameters
var index = args["startIndex"] != undefined ? parseInt(args["startIndex"]) : 0; var index = args["startIndex"] != undefined ? parseInt(args["startIndex"]) : 0;
var count = args["pageSize"] != undefined ? parseInt(args["pageSize"]) : 10; var count = args["pageSize"] != undefined ? parseInt(args["pageSize"]) : 10;
// begin and end date // begin and end date
var fromDate = null; var fromDate = null;
if (args["fromDate"] != undefined) if (args["fromDate"] != undefined)
{ {
var tmp = parseInt(args["fromDate"]); var tmp = parseInt(args["fromDate"]);
if (tmp != Number.NaN) if (tmp != Number.NaN)
{ {
fromDate = new Date(tmp); fromDate = new Date(tmp);
} }
} }
var toDate = null; var toDate = null;
if (args["toDate"] != undefined) if (args["toDate"] != undefined)
{ {
var tmp = parseInt(args["toDate"]); var tmp = parseInt(args["toDate"]);
if (tmp != Number.NaN) if (tmp != Number.NaN)
{ {
toDate = new Date(tmp); toDate = new Date(tmp);
} }
} }
model.data = getBlogPostList(node, fromDate, toDate, index, count); // fetch and assign the data
model.data = getBlogPostList(node, fromDate, toDate, index, count);
model.contentFormat = (args["contentFormat"] != undefined) ? args["contentFormat"] : "full"; model.contentFormat = (args["contentFormat"] != undefined) ? args["contentFormat"] : "full";
} }
main(); main();

View File

@@ -3,14 +3,13 @@
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/blogs/blogpost.lib.js"> <import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/blogs/blogpost.lib.js">
/** /**
* Creates a post inside the passed forum node. * Creates a blog post
*/ */
function createBlogPost(blogNode) function createBlogPost(blogNode)
{ {
// fetch the data required to create a topic // fetch the data required to create the post
var title = json.get("title"); var title = json.get("title");
var content = json.get("content"); var content = json.get("content");
logger.log("Creating new blog post " + title + " with text " + content);
// get a unique name // get a unique name
var name = getUniqueChildName(blogNode, "post"); var name = getUniqueChildName(blogNode, "post");

View File

@@ -2,37 +2,32 @@
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/comments/comments.lib.js"> <import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/comments/comments.lib.js">
/** /**
* Deletes a topic node. * Delete a comment.
*/ */
function deleteComment(node) function deleteComment(node)
{ {
// we simply delete the topic // we simply delete the topic
var qnamePath = node.qnamePath; var nodeRef = node.nodeRef;
logger.log("Deleting node " + qnamePath); var isDeleted = node.remove();
var isDeleted = node.remove(); if (! isDeleted)
logger.log("Node deleted: " + isDeleted); {
if (! isDeleted) status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, "Unable to delete node: " + nodeRef);
{ return;
status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, "Unable to delete node: " + qnamePath); }
return;
} model.message = "Node " + nodeRef + " deleted";
// also remove the discussable aspect if there are no more comments
deleteCommentsFolder(node);
model.message = "Node " + qnamePath + " deleted";
} }
function main() function main()
{ {
// get requested node // get requested node
var node = getRequestNode(); var node = getRequestNode();
if (status.getCode() != status.STATUS_OK) if (status.getCode() != status.STATUS_OK)
{ {
return; return;
} }
deleteComment(node); deleteComment(node);
} }
main(); main();

View File

@@ -3,16 +3,14 @@
function main() function main()
{ {
// get requested node // get requested node
var node = getRequestNode(); var node = getRequestNode();
if (status.getCode() != status.STATUS_OK) if (status.getCode() != status.STATUS_OK)
{ {
return; return;
} }
model.item = node; model.item = getCommentData(node);
// process additional parameters
} }
main(); main();

View File

@@ -2,37 +2,37 @@
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/comments/comments.lib.js"> <import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/comments/comments.lib.js">
/** /**
* Creates a post inside the passed forum node. * Update a comment
*/ */
function updateComment(node) function updateComment(node)
{ {
/*var title = ""; var title = "";
if (json.has("title")) if (json.has("title"))
{ {
title = json.get("title"); title = json.get("title");
}*/ }
var content = json.get("content"); var content = json.get("content");
// update the topic title // update the topic title
//postNode.properties.title = title; node.properties.title = title;
node.mimetype = "text/html"; node.mimetype = "text/html";
node.content = content; node.content = content;
node.save(); node.save();
} }
function main() function main()
{ {
// get requested node // get requested node
var node = getRequestNode(); var node = getRequestNode();
if (status.getCode() != status.STATUS_OK) if (status.getCode() != status.STATUS_OK)
{ {
return; return;
} }
// update // update comment
updateComment(node); updateComment(node);
model.item = node; model.item = getCommentData(node);
} }
main(); main();

View File

@@ -1,39 +1,31 @@
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/requestutils.lib.js"> <import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/requestutils.lib.js">
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/generic-paged-results.lib.js">
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/comments/comments.lib.js"> <import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/comments/comments.lib.js">
/** /**
* Fetches all posts found in the forum. * Get all comments for a node
*/ */
function getCommentsList(node, index, count) function getCommentsList(node, index, count)
{ {
var nodes = new Array(); var nodes = getComments(node);
// comments are added through a custom child association ("cm:comments") that links to a cm:folder that holds all the comment files
var commentFolder = getCommentsFolder(node);
if (commentFolder != null)
{
nodes = commentFolder.childAssocs["cm:contains"];
}
return getCommentListData(nodes, index, count); return getPagedResultsData(nodes, index, count, getCommentData);
} }
function main() function main()
{ {
// get requested node // get requested node
var node = getRequestNode(); var node = getRequestNode();
if (status.getCode() != status.STATUS_OK) if (status.getCode() != status.STATUS_OK)
{ {
return; return;
} }
// process additional parameters // process additional parameters
var index = args["startIndex"] != undefined ? parseInt(args["startIndex"]) : 0; var index = args["startIndex"] != undefined ? parseInt(args["startIndex"]) : 0;
var count = args["pageSize"] != undefined ? parseInt(args["pageSize"]) : 10; var count = args["pageSize"] != undefined ? parseInt(args["pageSize"]) : 10;
model.data = getCommentsList(node, index, count); model.data = getCommentsList(node, index, count);
} }
main(); main();

View File

@@ -1,23 +1,44 @@
/** Name used for the topic that contains all comments. */
const COMMENTS_TOPIC_NAME = "Comments";
/**
* Returns all comment nodes for a given node.
* @return an array of comments.
*/
function getComments(node)
{
var commentsFolder = getCommentsFolder(node);
if (commentsFolder != null)
{
var elems = commentsFolder.childAssocs["cm:contains"];
if (elems != null)
{
return elems;
}
}
// no comments found, return an empty array
return new Array();
}
/** /**
* Returns the folder that contains all the comments. * Returns the folder that contains all the comments.
* We currently use the forum model for testing purpose *
* PENDING: use a proper model! * We currently use the fm:discussable aspect where we
*/ * add a "Comments" topic to it.
*/
function getCommentsFolder(node) function getCommentsFolder(node)
{ {
if (node.hasAspect("fm:discussable")) if (node.hasAspect("fm:discussable"))
{ {
var forumFolder = node.childAssocs["fm:discussion"][0]; var forumFolder = node.childAssocs["fm:discussion"][0];
// we simply take the first topic folder in it var topicFolder = forumFolder.childByNamePath(COMMENTS_TOPIC_NAME);
// PENDING: this is error prone! return topicFolder;
var topicFolder = forumFolder.childAssocs["cm:contains"][0]; }
return topicFolder; else
} {
else return null;
{ }
return null;
}
} }
/** /**
@@ -25,75 +46,31 @@ function getCommentsFolder(node)
*/ */
function getOrCreateCommentsFolder(node) function getOrCreateCommentsFolder(node)
{ {
var commentsFolder = getCommentsFolder(node); var commentsFolder = getCommentsFolder(node);
if (commentsFolder != null) if (commentsFolder != null)
{ {
return commentsFolder; return commentsFolder;
} }
node.addAspect("fm:discussable"); // add the aspect and create the forum as well as the comments topic
var forumNode = node.createNode("Comments Forum", "fm:forum", "fm:discussion"); node.addAspect("fm:discussable");
commentsFolder = forumNode.createNode("Comments", "fm:topic", "cm:contains"); var forumNode = node.createNode("Forum", "fm:forum", "fm:discussion");
return commentsFolder; commentsFolder = forumNode.createNode(COMMENTS_TOPIC_NAME, "fm:topic", "cm:contains");
return commentsFolder;
} }
/** /**
* Deletes the comments folder for a node if there are no comments in it. * Returns the data object for a comment node
*/ */
function deleteCommentsFolder(node)
{
var commentsFolder = getCommentFolder(node);
if (commentsFolder != null && commentsFolder.childAssocs["cm:contains"] == 0)
{
var forumFolder = node.childAssocs["fm:discussion"][0];
node.removeNode(forumNode);
node.removeAspect("fm:discussable");
}
}
function getCommentData(node) function getCommentData(node)
{ {
return node; return node;
} }
/** /**
* Returns an array containing all topics found in the passed array. * Returns the count of comments for a node.
* Filters out non-fm:topic nodes.
*/ */
function getCommentListData(nodes, index, count) function getCommentsCount(node)
{ {
var items = new Array(); return getComments(node).length;
var i;
var added = 0;
for (i = index; i < nodes.length && added < count; i++)
{
items.push(getCommentData(nodes[i]));
added++;
}
return ({
"total" : nodes.length,
"pageSize" : count,
"startIndex" : index,
"itemCount" : items.length,
"items": items
});
} }
/**
* Returns a list of topics, as returned by the lucene query
*/
/*function getBlogsListByLuceneQuery(node, luceneQuery, sortAttribute, ascending, index, count)
{
var nodes = null;
if (sortAttribute != null)
{
nodes = search.luceneSearch(node.nodeRef.storeRef.toString(), luceneQuery, sortAttribute, ascending);
}
else
{
nodes = search.luceneSearch(luceneQuery);
}
return getBlogListData(nodes, index, count);
}*/

View File

@@ -7,37 +7,41 @@
*/ */
function addComment(node) function addComment(node)
{ {
// fetch the data required to create a comment // fetch the data required to create a comment
//var title = json.get("title"); var title = "";
var content = json.get("content"); if (json.has("title"))
logger.log("Creating new comment with text " + content); {
title = json.get("title");
}
var content = json.get("content");
var commentsFolder = getOrCreateCommentsFolder(node); // fetch the parent to add the node to
var commentsFolder = getOrCreateCommentsFolder(node);
// get a unique name // get a unique name
var name = getUniqueChildName(commentsFolder, "comment"); var name = getUniqueChildName(commentsFolder, "comment");
// we simply create a new file inside the blog folder // create the comment
var commentNode = commentsFolder.createNode(name, "fm:post"); var commentNode = commentsFolder.createNode(name, "fm:post");
commentNode.mimetype = "text/html"; commentNode.mimetype = "text/html";
//commentNode.properties.title = title; commentNode.properties.title = title;
commentNode.content = content; commentNode.content = content;
commentNode.save(); commentNode.save();
return commentNode; return commentNode;
} }
function main() function main()
{ {
// get requested node // get requested node
var node = getRequestNode(); var node = getRequestNode();
if (status.getCode() != status.STATUS_OK) if (status.getCode() != status.STATUS_OK)
{ {
return; return;
} }
var comment = addComment(node); var comment = addComment(node);
model.item = comment; model.item = getCommentData(comment);
} }
main(); main();

View File

@@ -1,88 +1,102 @@
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/requestutils.lib.js"> <import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/requestutils.lib.js">
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/generic-paged-results.lib.js">
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/discussions/topicpost.lib.js"> <import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/discussions/topicpost.lib.js">
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts.lib.js">
/** var MAX_NUM_OF_PROCESSED_POSTS = 20;
* Prepends a number with zeros to make it a 4numgth string
*/
function toFourDigitString(num)
{
if (num < 10) return "000" + num;
if (num < 100) return "00" + num;
if (num < 1000) return "0" + num;
return "" + num;
}
/** /**
* Fetches the hot topics found in the forum. * Fetches the hot topics found in the forum.
* Hot topics are the one with the most replies recently. * Hot topics are topics with the most replies over the last x days.
* *
* We implement this follows: We fetch all posts in the forum, ordered by inverse * The current implementation fetches all posts in the forum ordered by inverse
* creation date and fetch the nodes for the first 20 posts * creation date. It then analyzes the last x posts and fetches the topics thereof,
* keeping track of the number of posts for each.
*
* Note: We only look at topics with replies, the others will therefore not show up
* in that list.
*/ */
function getHotTopicPostList(node, index, count) function getHotTopicPostList(node, index, count)
{ {
var luceneQuery = " +TYPE:\"{http://www.alfresco.org/model/forum/1.0}post\"" + // get the posts to check
" +PATH:\"" + node.qnamePath + "/*/*\"" + var luceneQuery = " +TYPE:\"{http://www.alfresco.org/model/forum/1.0}post\"" +
" +ASPECT:\"cm:referencing\""; " +PATH:\"" + node.qnamePath + "/*/*\"" +
var sortAttribute = "@{http://www.alfresco.org/model/content/1.0}created"; " +ASPECT:\"cm:referencing\"";
posts = search.luceneSearch(node.nodeRef.storeRef.toString(), luceneQuery, sortAttribute, false); var sortAttribute = "@{http://www.alfresco.org/model/content/1.0}created";
var posts = search.luceneSearch(node.nodeRef.storeRef.toString(), luceneQuery, sortAttribute, false);
/** Implement sort and order logic to show the node with most replies first. */ // check how many posts we check in the result set
// PENDING: quick and dirty version, works but needs cleanup! var max = MAX_NUM_OF_PROCESSED_POSTS;
var max = 20; // only process so many posts if (posts.length < max)
if (posts.length < max) max = posts.length; {
max = posts.length;
var idToNode = new Array(); }
var idToCount = new Array();
// get for each the topic, keeping track of the number of replies and the first occurance
// of the post.
var idToData = {};
for (var x=0; x < max; x++)
{
// get the topic node (which is the direct parent of the post)
var parent = posts[x].parent;
var id = parent.nodeRef.id;
if (idToData[id] != null) {
idToData[id].count += 1;
} else {
idToData[id] = { count: 1, pos: x, node: parent};
}
}
// copy the elements to an array as we will have to sort it
// afterwards
var dataArr = new Array();
for (n in idToData)
{
dataArr.push(idToData[n]);
}
// sort the elements by number of replies, then by the position
var sorter = function(a, b)
{
if (a.count != b.count)
{
// more replies first
return b.count - a.count
}
else {
// lower pos first
return a.pos - b.pos;
}
}
dataArr.sort(sorter);
for (var x=0; x < max; x++) // extract now the nodes
{ var nodes = Array();
var parent = posts[x].parent; for (var x=0; x < dataArr.length; x++)
var id = parent.nodeRef.id; {
if (idToCount[id] != null) { nodes.push(dataArr[x].node);
idToCount[id] = idToCount[id] + 1; }
} else {
idToNode[id] = parent;
idToCount[id] = 1;
}
}
// get the list sorted by number of replies
var tmp = new Array();
for (var id in idToCount) {
tmp.push(toFourDigitString(idToCount[id]) + id);
}
tmp.sort();
tmp.reverse();
var nodes = Array();
for (var x=0; x < tmp.length; x++)
{
nodes.push(idToNode[tmp[x].substring(4)]);
}
// get the data // get the paginated data
return getTopicPostListData(nodes, index, count); return getPagedResultsData(nodes, index, count, getTopicPostData);
} }
function main() function main()
{ {
// get requested node // get requested node
var node = getRequestNode(); var node = getRequestNode();
if (status.getCode() != status.STATUS_OK) if (status.getCode() != status.STATUS_OK)
{ {
return; return;
} }
// process additional parameters // process additional parameters
var index = args["startIndex"] != undefined ? parseInt(args["startIndex"]) : 0; var index = args["startIndex"] != undefined ? parseInt(args["startIndex"]) : 0;
var count = args["pageSize"] != undefined ? parseInt(args["pageSize"]) : 10; var count = args["pageSize"] != undefined ? parseInt(args["pageSize"]) : 10;
// fetch the data and assign it to the model // fetch the data and assign it to the model
model.data = getHotTopicPostList(node, index, count); model.data = getHotTopicPostList(node, index, count);
model.contentFormat = (args["contentFormat"] != undefined) ? args["contentFormat"] : "full"; model.contentFormat = (args["contentFormat"] != undefined) ? args["contentFormat"] : "full";
} }
main(); main();

View File

@@ -1,32 +1,17 @@
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/requestutils.lib.js"> <import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/requestutils.lib.js">
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/generic-paged-results.lib.js">
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/discussions/topicpost.lib.js"> <import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/discussions/topicpost.lib.js">
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts.lib.js">
function getLatestAddedPostData(node)
function getLatestAddedPostsListData(nodes, index, count)
{ {
var items = new Array(); // fetch the topic post data and then add the node as reply.
var i; // in case of a new topic post, the reply and the post will be the
var added = 0; // same, otherwise they differ. The topic post will be used to fetch
for (i = index; i < nodes.length && added < count; i++) // the topic title, while the reply will be used to show the text
{ var data = getTopicPostData(node.parent);
// fetch the topic post data and then add the node as reply. data.reply = node;
// in case of a new topicpost, the reply and the post will be the data.isRootPost = data.post.nodeRef == data.reply.nodeRef;
// same, otherwise they differ. The topic post will be used to fetch return data;
// the topic title, while the reply will be used to show the text
var data = getTopicPostData(nodes[i].parent);
data.reply = nodes[i];
items.push(data);
added++;
}
return ({
"total" : nodes.length,
"pageSize" : count,
"startIndex" : index,
"itemCount" : items.length,
"items": items
});
} }
/** /**
@@ -37,10 +22,9 @@ function getLatestAddedPosts(node, index, count)
var luceneQuery = " +TYPE:\"{http://www.alfresco.org/model/forum/1.0}post\"" + var luceneQuery = " +TYPE:\"{http://www.alfresco.org/model/forum/1.0}post\"" +
" +PATH:\"" + node.qnamePath + "/*/*\""; " +PATH:\"" + node.qnamePath + "/*/*\"";
var sortAttribute = "@{http://www.alfresco.org/model/content/1.0}created"; var sortAttribute = "@{http://www.alfresco.org/model/content/1.0}created";
nodes = search.luceneSearch(node.nodeRef.storeRef.toString(), luceneQuery, sortAttribute, false);
// get the data // get the data
return getLatestAddedPostsListData(nodes, index, count); return getPagedResultsDataByLuceneQuery(node, luceneQuery, sortAttribute, false, index, count, getLatestAddedPostData);
} }
function main() function main()
@@ -63,4 +47,3 @@ function main()
} }
main(); main();
var x = 0;

View File

@@ -4,6 +4,7 @@
<#macro latestPostJSON item> <#macro latestPostJSON item>
{ {
"topicTitle" : "${item.post.properties.title?js_string}", "topicTitle" : "${item.post.properties.title?js_string}",
"isRootPost" : ${item.isRootPost?string},
<@postLib.postDataJSON refNode=item.topic post=item.reply /> <@postLib.postDataJSON refNode=item.topic post=item.reply />
} }
</#macro> </#macro>

View File

@@ -1,11 +1,12 @@
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/requestutils.lib.js"> <import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/requestutils.lib.js">
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/searchutils.lib.js"> <import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/searchutils.lib.js">
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/generic-paged-results.lib.js">
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/discussions/topicpost.lib.js"> <import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/discussions/topicpost.lib.js">
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts.lib.js">
const DEFAULT_NUM_DAYS = 30;
/** /**
* Returns the date object for the NOW - numdays days in the past * Returns the date object for the date "numdays" ago
*/ */
function getTodayMinusXDays(numdays) function getTodayMinusXDays(numdays)
{ {
@@ -17,42 +18,40 @@ function getTodayMinusXDays(numdays)
} }
/** /**
* Fetches all posts found in the forum. * Fetches all posts added to the forum in the last numdays days
*/ */
function getTopicPostList(node, numdays, index, count) function getTopicPostList(node, numdays, index, count)
{ {
var fromDate = getTodayMinusXDays(numdays); var fromDate = getTodayMinusXDays(numdays);
// query information // query information
var luceneQuery = " +TYPE:\"{http://www.alfresco.org/model/forum/1.0}topic\"" + var luceneQuery = " +TYPE:\"{http://www.alfresco.org/model/forum/1.0}topic\"" +
" +PATH:\"" + node.qnamePath + "/*\" " + " +PATH:\"" + node.qnamePath + "/*\" " +
getCreationDateRangeQuery(fromDate, null); getCreationDateRangeQuery(fromDate, null);
var sortAttribute = "@{http://www.alfresco.org/model/content/1.0}created"; var sortAttribute = "@{http://www.alfresco.org/model/content/1.0}created";
// get the data // get the data
return getTopicPostListByLuceneQuery(node, luceneQuery, sortAttribute, false, index, count); return getPagedResultsDataByLuceneQuery(node, luceneQuery, sortAttribute, false, index, count, getTopicPostData);
} }
function main() function main()
{ {
// get requested node // get requested node
var node = getRequestNode(); var node = getRequestNode();
if (status.getCode() != status.STATUS_OK) if (status.getCode() != status.STATUS_OK)
{ {
return; return;
} }
// process additional parameters // process additional parameters
var index = args["startIndex"] != undefined ? parseInt(args["startIndex"]) : 0; var index = args["startIndex"] != undefined ? parseInt(args["startIndex"]) : 0;
var count = args["pageSize"] != undefined ? parseInt(args["pageSize"]) : 10; var count = args["pageSize"] != undefined ? parseInt(args["pageSize"]) : 10;
var numdays = args["numdays"] != undefined ? parseInt(args["numdays"]) : DEFAULT_NUM_DAYS;
var DEFAULT_NUM_DAYS = 30;
var numdays = args["numdays"] != undefined ? parseInt(args["numdays"]) : DEFAULT_NUM_DAYS; // fetch the data and assign it to the model
model.data = getTopicPostList(node, numdays, index, count);
// fetch the data and assign it to the model
model.data = getTopicPostList(node, numdays, index, count); model.contentFormat = (args["contentFormat"] != undefined) ? args["contentFormat"] : "full";
model.contentFormat = (args["contentFormat"] != undefined) ? args["contentFormat"] : "full";
} }
main(); main();

View File

@@ -1,37 +1,37 @@
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/requestutils.lib.js"> <import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/requestutils.lib.js">
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/generic-paged-results.lib.js">
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/discussions/topicpost.lib.js"> <import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/discussions/topicpost.lib.js">
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts.lib.js">
/** /**
* Fetches all posts found in the forum. * Fetches all posts found in the forum.
*/ */
function getTopicPostList(node, index, count) function getTopicPostList(node, index, count)
{ {
// query information // query information
var luceneQuery = " +TYPE:\"{http://www.alfresco.org/model/forum/1.0}topic\"" + var luceneQuery = " +TYPE:\"{http://www.alfresco.org/model/forum/1.0}topic\"" +
" +PATH:\"" + node.qnamePath + "/*\" "; " +PATH:\"" + node.qnamePath + "/*\" ";
var sortAttribute = "@{http://www.alfresco.org/model/content/1.0}created"; var sortAttribute = "@{http://www.alfresco.org/model/content/1.0}created";
// get the data // get the data
return getTopicPostListByLuceneQuery(node, luceneQuery, sortAttribute, false, index, count); return getPagedResultsDataByLuceneQuery(node, luceneQuery, sortAttribute, false, index, count, getTopicPostData);
} }
function main() function main()
{ {
// get requested node // get requested node
var node = getRequestNode(); var node = getRequestNode();
if (status.getCode() != status.STATUS_OK) if (status.getCode() != status.STATUS_OK)
{ {
return; return;
} }
// process additional parameters // process additional parameters
var index = args["startIndex"] != undefined ? parseInt(args["startIndex"]) : 0; var index = args["startIndex"] != undefined ? parseInt(args["startIndex"]) : 0;
var count = args["pageSize"] != undefined ? parseInt(args["pageSize"]) : 10; var count = args["pageSize"] != undefined ? parseInt(args["pageSize"]) : 10;
model.data = getTopicPostList(node, index, count); model.data = getTopicPostList(node, index, count);
model.contentFormat = (args["contentFormat"] != undefined) ? args["contentFormat"] : "full"; model.contentFormat = (args["contentFormat"] != undefined) ? args["contentFormat"] : "full";
} }
main(); main();

View File

@@ -1,42 +0,0 @@
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/discussions/topicpost.lib.js">
/**
* Returns an array containing all topics found in the passed array.
* Filters out non-fm:topic nodes.
*/
function getTopicPostListData(nodes, index, count)
{
var items = new Array();
var i;
var added = 0;
for (i = index; i < nodes.length && added < count; i++)
{
items.push(getTopicPostData(nodes[i]));
added++;
}
return ({
"total" : nodes.length,
"pageSize" : count,
"startIndex" : index,
"itemCount" : items.length,
"items": items
});
}
/**
* Returns a list of topics, as returned by the lucene query
*/
function getTopicPostListByLuceneQuery(node, luceneQuery, sortAttribute, ascending, index, count)
{
var nodes = null;
if (sortAttribute != null)
{
nodes = search.luceneSearch(node.nodeRef.storeRef.toString(), luceneQuery, sortAttribute, ascending);
}
else
{
nodes = search.luceneSearch(luceneQuery);
}
return getTopicPostListData(nodes, index, count);
}

View File

@@ -3,17 +3,18 @@
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/discussions/topicpost.lib.js"> <import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/discussions/topicpost.lib.js">
/** /**
* Creates a post inside the passed forum node. * Adds a post to the passed forum node.
*/ */
function createPost(forumNode) function createPost(forumNode)
{ {
// fetch the data required to create a topic // fetch the data required to create a topic
var title = json.get("title"); var title = json.get("title");
var content = json.get("content"); var content = json.get("content");
logger.log("Creating New post " + title + " with text " + content);
// create the topic node, and add the first child node representing the topic text // create the topic node, and add the first child node representing the topic text
// NOTE: this is a change from the old web client, where the topic title was used as name for the node // NOTE: this is a change from the old web client, where the topic title was used as name
// for the topic node. We will use generated names to make sure we won't have naming
// clashes.
var name = getUniqueChildName(forumNode, "post"); var name = getUniqueChildName(forumNode, "post");
var topicNode = forumNode.createNode(name, "fm:topic"); var topicNode = forumNode.createNode(name, "fm:topic");
@@ -29,15 +30,16 @@ function createPost(forumNode)
function main() function main()
{ {
// get requested node // get requested node
var node = getRequestNode(); var node = getRequestNode();
if (status.getCode() != status.STATUS_OK) if (status.getCode() != status.STATUS_OK)
{ {
return; return;
} }
var topicPost = createPost(node); var topicPost = createPost(node);
model.topicpost = getTopicPostData(topicPost);
model.topicpost = getTopicPostData(topicPost);
} }
main(); main();

View File

@@ -2,89 +2,95 @@
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/discussions/topicpost.lib.js"> <import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/discussions/topicpost.lib.js">
/** /**
* Fetches the nodes that are children of post * Returns all reply nodes to a post
*/ */
function getRepliesForPost(post) function getRepliesForPost(post)
{ {
var children = post.sourceAssocs["cm:references"]; var children = post.sourceAssocs["cm:references"];
if (children === null) if (children === null)
{ {
return new Array(); return new Array();
} }
else else
{ {
return children; return children;
} }
} }
/** /**
* Fetches the reply node and the child count. * Returns a data object containing the passed post,
* Fetches the children in addition if levels > 1. * the number of replies to the post, as well as
* the replies themselves if levels > 1
*/ */
function getReplyDataRecursive(post, levels) function getReplyDataRecursive(post, levels)
{ {
// encapsulates the data: node, childCount, children // encapsulates the data: node, childCount, children
var data = new Object(); var data = new Object();
data.post = post; data.post = post;
var children = getRepliesForPost(post); var children = getRepliesForPost(post);
data.childCount = children.length; data.childCount = children.length;
if (levels > 1) if (levels > 1)
{ {
data.children = new Array(); data.children = new Array();
var x = 0; var x = 0;
for (x =0; x < children.length; x++) for (x =0; x < children.length; x++)
{ {
data.children.push(getReplyDataRecursive(children[x], levels-1)); data.children.push(getReplyDataRecursive(children[x], levels-1));
} }
} }
return data; return data;
} }
/**
* Returns a data object containing all replies of a post.
* @return data object with "children" property that contains an array of reply data objects
*/
function getRepliesImpl(post, levels) function getRepliesImpl(post, levels)
{ {
var data = getReplyDataRecursive(post, levels + 1); var data = getReplyDataRecursive(post, levels + 1);
if (data.children != undefined) if (data.children != undefined)
{ {
return data.children; return data.children;
} }
else else
{ {
return new Array(); return new Array();
} }
} }
function getReplies(node, levels) function getReplies(node, levels)
{ {
// we have to differentiate here whether this is a top-level post or a reply // we have to differentiate here whether this is a top-level post or a reply
if (node.type == "{http://www.alfresco.org/model/forum/1.0}topic") if (node.type == "{http://www.alfresco.org/model/forum/1.0}topic")
{ {
// find the primary post node // find the primary post node.
var data = getTopicPostData(node); // PENDING: couldn't this be done in a more performant way? var data = getTopicPostData(node);
return getRepliesImpl(data.post, levels); return getRepliesImpl(data.post, levels);
} }
else if (node.type == "{http://www.alfresco.org/model/forum/1.0}post") else if (node.type == "{http://www.alfresco.org/model/forum/1.0}post")
{ {
return getRepliesImpl(node, levels); // the node is already a post
} return getRepliesImpl(node, levels);
else }
{ else
status.setCode(STATUS_BAD_REQUEST, "Incompatible node type. Required either fm:topic or fm:post. Received: " + node.type); {
} status.setCode(STATUS_BAD_REQUEST, "Incompatible node type. Required either fm:topic or fm:post. Received: " + node.type);
}
} }
function main() function main()
{ {
// get requested node // get requested node
var node = getRequestNode(); var node = getRequestNode();
if (status.getCode() != status.STATUS_OK) if (status.getCode() != status.STATUS_OK)
{ {
return; return;
} }
// process additional parameters // process additional parameters
var levels = args["levels"] != undefined ? parseInt(args["levels"]) : 1; var levels = args["levels"] != undefined ? parseInt(args["levels"]) : 1;
model.data = getReplies(node, levels); model.data = getReplies(node, levels);
} }
main(); main();

View File

@@ -5,69 +5,70 @@
/** /**
* Creates a post inside the passed forum node. * Creates a post inside the passed forum node.
*/ */
function createReplyPost(topicNode, parentPostNode) function createPostReplyImpl(topicNode, parentPostNode)
{ {
// fetch the data required to create a topic // fetch the data required to create a topic
var title = ""; var title = "";
if (json.has("title")) if (json.has("title"))
{ {
title = json.get("title"); title = json.get("title");
} }
var content = json.get("content"); var content = json.get("content");
logger.log("Creating new post with title " + title + " and text " + content);
// create the topic node, and add the first child node representing the topic text // create the post node using a unique name
// NOTE: this is a change from the old web client, where the topic title was used as name for the node var name = getUniqueChildName(topicNode, "post");
var name = getUniqueChildName(topicNode, "post"); var postNode = topicNode.createNode(name, "fm:post");
postNode.mimetype = "text/html";
var postNode = topicNode.createNode(name, "fm:post"); postNode.properties.title = title;
postNode.mimetype = "text/html"; postNode.content = content;
postNode.properties.title = title; postNode.save();
postNode.content = content;
postNode.save();
// link it to the parent post // link it to the parent post
postNode.addAspect("cm:referencing"); postNode.addAspect("cm:referencing");
postNode.createAssociation(parentPostNode, "cm:references"); postNode.createAssociation(parentPostNode, "cm:references");
postNode.save(); // probably not necessary here postNode.save(); // probably not necessary here
return postNode; return postNode;
} }
/**
* Creates a reply to a post.
* @param node The parent post node
*/
function createPostReply(node) function createPostReply(node)
{ {
// we have to differentiate here whether this is a top-level post or a reply // we have to differentiate here whether this is a top-level post or a reply
if (node.type == "{http://www.alfresco.org/model/forum/1.0}topic") if (node.type == "{http://www.alfresco.org/model/forum/1.0}topic")
{ {
// find the primary post node // find the primary post node
var data = getTopicPostData(node); var topic = node;
var topic = data.topic; var post = findPostNode(node);
var post = data.post; return createPostReplyImpl(topic, post);
return createReplyPost(topic, post); }
} else if (node.type == "{http://www.alfresco.org/model/forum/1.0}post")
else if (node.type == "{http://www.alfresco.org/model/forum/1.0}post") {
{ // the forum is the parent of the node
// the forum is the paren of the node var topic = node.parent;
var topic = node.parent; var post = node;
var post = node; return createPostReplyImpl(topic, post);
return createReplyPost(topic, post); }
} else
else {
{ status.setCode(STATUS_BAD_REQUEST, "Incompatible node type. Required either fm:topic or fm:post. Received: " + node.type);
status.setCode(STATUS_BAD_REQUEST, "Incompatible node type. Required either fm:topic or fm:post. Received: " + node.type); return null;
} }
} }
function main() function main()
{ {
// get requested node // get requested node
var node = getRequestNode(); var node = getRequestNode();
if (status.getCode() != status.STATUS_OK) if (status.getCode() != status.STATUS_OK)
{ {
return; return;
} }
model.post = createPostReply(node); model.post = createPostReply(node);
} }
main(); main();

View File

@@ -1,61 +1,40 @@
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/requestutils.lib.js"> <import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/requestutils.lib.js">
const DELETED_REPLY_POST_MARKER = "[[deleted]]";
/** /**
* Deletes a topic node. * Deletes a topic node.
*/ */
function deleteTopicPost(topicNode) function deleteTopicPost(topicNode)
{ {
// we simply delete the topic // we simply delete the complete topic
var qnamePath = topicNode.qnamePath; var nodeRef = topicNode.nodeRef;
logger.log("Deleting node " + qnamePath); var isDeleted = topicNode.remove();
var isDeleted = topicNode.remove(); if (! isDeleted)
logger.log("Node deleted: " + isDeleted); {
if (! isDeleted) status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, "Unable to delete node: " + nodeRef);
{ return;
status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, "Unable to delete node: " + qnamePath); }
return; model.message = "Node " + nodeRef + " deleted";
}
model.message = "Node " + qnamePath + " deleted";
} }
function addRepliesOfPostRecursive(node, arr)
{
var replies = node.sourceAssocs["cm:references"];
if (replies != null)
{
var x;
for (x = 0; x < replies.length; x++)
{
addRepliesOfPostRecursive(replies[x], arr);
arr.push(replies[x]);
}
}
}
/** /**
* Deletes a reply post. * Delete a reply post.
* Note: Because posts are recursive, we can't simply delete the node.
* For now we set a marker text [[delete]] as title and content.
*/ */
function deleteReplyPost(postNode) function deleteReplyPost(node)
{ {
// we have to delete the node as well as all replies var title = DELETED_REPLY_POST_MARKER;
// PENDING: what happens if the user has the right to delete its node var content = DELETED_REPLY_POST_MARKER;
// but not the replies to it?
var nodes = new Array(); // update the topic title
addRepliesOfPostRecursive(postNode, nodes); node.properties.title = title;
nodes.push(postNode); node.mimetype = "text/html";
node.content = content;
var qnamePath = postNode.qnamePath node.save();
var isDeleted = false;
for (x = 0; x < nodes.length; x++) model.message = "Node " + node.nodeRef + " marked as removed";
{
isDeleted = nodes[x].remove();
if (! isDeleted)
{
status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, "Unable to delete node: " + nodes[x].nodeRef);
return;
}
}
model.message = "Node " + qnamePath + " deleted";
} }
/** /**
@@ -64,31 +43,31 @@ function deleteReplyPost(postNode)
*/ */
function deletePost(node) function deletePost(node)
{ {
// simple case: topic post // simple case: topic post
if (node.type == "{http://www.alfresco.org/model/forum/1.0}topic") if (node.type == "{http://www.alfresco.org/model/forum/1.0}topic")
{ {
deleteTopicPost(node); deleteTopicPost(node);
} }
else if (node.type == "{http://www.alfresco.org/model/forum/1.0}post") else if (node.type == "{http://www.alfresco.org/model/forum/1.0}post")
{ {
deleteReplyPost(node); deleteReplyPost(node);
} }
else else
{ {
status.setCode(status.STATUS_BAD_REQUEST, "Node is not of type fm:topic or fm:post"); status.setCode(status.STATUS_BAD_REQUEST, "Node is not of type fm:topic or fm:post");
} }
} }
function main() function main()
{ {
// get requested node // get requested node
var node = getRequestNode(); var node = getRequestNode();
if (status.getCode() != status.STATUS_OK) if (status.getCode() != status.STATUS_OK)
{ {
return; return;
} }
deletePost(node); deletePost(node);
} }
main(); main();

View File

@@ -6,32 +6,32 @@
*/ */
function fetchPostData(node) function fetchPostData(node)
{ {
// we have to differentiate here whether this is a top-level post or a reply // 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) // 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") if (node.type == "{http://www.alfresco.org/model/forum/1.0}topic")
{ {
model.topicpost = getTopicPostData(node); model.topicpost = getTopicPostData(node);
} }
else if (node.type == "{http://www.alfresco.org/model/forum/1.0}post") else if (node.type == "{http://www.alfresco.org/model/forum/1.0}post")
{ {
model.post = node; model.post = node;
} }
else else
{ {
status.setCode(STATUS_BAD_REQUEST, "Incompatible node type. Required either fm:topic or fm:post. Received: " + node.type); status.setCode(STATUS_BAD_REQUEST, "Incompatible node type. Required either fm:topic or fm:post. Received: " + node.type);
} }
} }
function main() function main()
{ {
// get requested node // get requested node
var node = getRequestNode(); var node = getRequestNode();
if (status.getCode() != status.STATUS_OK) if (status.getCode() != status.STATUS_OK)
{ {
return; return;
} }
fetchPostData(node); fetchPostData(node);
} }
main(); main();

View File

@@ -2,107 +2,80 @@
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/discussions/topicpost.lib.js"> <import resource="classpath:alfresco/templates/webscripts/org/alfresco/repository/discussions/topicpost.lib.js">
/** /**
* Returns the correct post node to update- * Updates the passed forum post node.
* This function makes sure that a post is returned in case the passed node is a topic.
*/ */
function findPostNode(node) function updatePost(node)
{ {
if (node.type == "{http://www.alfresco.org/model/forum/1.0}post") var title = "";
{ if (json.has("title"))
return node; {
} title = json.get("title");
else if (node.type == "{http://www.alfresco.org/model/forum/1.0}topic") }
{ var content = json.get("content");
var nodes = getOrderedPosts(node);
if (nodes.length > 0) // update the topic title
{ node.properties.title = title;
return nodes[0]; node.mimetype = "text/html";
} node.content = content;
else node.save();
{
status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, "First post of topic node" + node.nodeRef + " missing");
}
}
else
{
status.setCode(STATUS_BAD_REQUEST, "Incompatible node type. Required either fm:topic or fm:post. Received: " + node.type);
}
}
/**
* Creates a post inside the passed forum node.
*/
function updatePost(postNode)
{
var title = "";
if (json.has("title"))
{
title = json.get("title");
}
var content = json.get("content");
// update the topic title
postNode.properties.title = title;
postNode.mimetype = "text/html";
postNode.content = content;
postNode.save();
} }
function main() function main()
{ {
// get requested node // get requested node
var node = getRequestNode(); var node = getRequestNode();
if (status.getCode() != status.STATUS_OK) if (status.getCode() != status.STATUS_OK)
{ {
return; return;
} }
// find the post node - returns the passed node in case node is a post, // find the post node - returns the passed node in case node is a post,
// or the primary post in case node is a topic // or the primary post in case node is a topic
/* Due to https://issues.alfresco.com/browse/ALFCOM-1775 /* Due to https://issues.alfresco.com/browse/ALFCOM-1775
we will do the search here as we have to reuse the lucene result later we will do the search here as we have to reuse the lucene result later
var postNode = findPostNode(node); var postNode = findPostNode(node);
if (status.getCode() != status.STATUS_OK) if (status.getCode() != status.STATUS_OK)
{ {
return; return;
}*/ }*/
var postNode = null; var postNode = null;
if (node.type == "{http://www.alfresco.org/model/forum/1.0}post") if (node.type == "{http://www.alfresco.org/model/forum/1.0}post")
{ {
postNode = node; postNode = node;
} }
else if (node.type == "{http://www.alfresco.org/model/forum/1.0}topic") else if (node.type == "{http://www.alfresco.org/model/forum/1.0}topic")
{ {
var nodes = getOrderedPosts(node); var nodes = getOrderedPosts(node);
if (nodes.length > 0) if (nodes.length > 0)
{ {
postNode = nodes[0]; postNode = nodes[0];
} }
else else
{ {
status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, "First post of topic node" + node.nodeRef + " missing"); status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, "First post of topic node" + node.nodeRef + " missing");
return; return;
} }
} }
else else
{ {
status.setCode(STATUS_BAD_REQUEST, "Incompatible node type. Required either fm:topic or fm:post. Received: " + node.type); status.setCode(STATUS_BAD_REQUEST, "Incompatible node type. Required either fm:topic or fm:post. Received: " + node.type);
return; return;
} }
// update // update
updatePost(postNode); updatePost(postNode);
// fetch the data to render the result // fetch the data to render the result
if (node.nodeRef.equals(postNode.nodeRef)) // false for topic posts if (node.nodeRef.equals(postNode.nodeRef)) // false for topic posts
{ {
model.post = node; model.post = node;
} }
else else
{ {
//model.topicpost = getTopicPostData(node); // See above, use getTopicPostDataFromTopicAndPosts instead of getTopicPostData
model.topicpost = getTopicPostDataFromTopicAndPosts(node, nodes); //model.topicpost = getTopicPostData(node);
} model.topicpost = getTopicPostDataFromTopicAndPosts(node, nodes);
}
} }
main(); main();

View File

@@ -1,12 +1,33 @@
/** /**
* This library contains functions to work with topics (aka top level posts). * Returns the fm:post node given a fm:topic or fm:post node.
* *
* Top-level posts are different from post that act as replies in that * This function makes sure that a post is returned in case the passed node is a topic.
* they have some additional data available:
* lastReplyOn:
* lastReplyFrom:
* totalReplyCount:
*/ */
function findPostNode(node)
{
if (node.type == "{http://www.alfresco.org/model/forum/1.0}post")
{
return node;
}
else if (node.type == "{http://www.alfresco.org/model/forum/1.0}topic")
{
var nodes = getOrderedPosts(node);
if (nodes.length > 0)
{
return nodes[0];
}
else
{
status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, "First post of topic node" + node.nodeRef + " missing");
}
}
else
{
status.setCode(STATUS_BAD_REQUEST, "Incompatible node type. Required either fm:topic or fm:post. Received: " + node.type);
return null;
}
}
/** Returns the topic posts for a topic, ordered by creation date. /** Returns the topic posts for a topic, ordered by creation date.

View File

@@ -0,0 +1,51 @@
/**
* Returns a data object that can be passed to the paged results template
* for rendering.
*
* @param nodes: The complete result set nodes
* @param index: the start index from which results should be returned
* @param count: the number of elements that should be returned
* @param extractDataFn: The function that extracts the data to be returned
* for each node in the final data set.
* The functions signature is name(index, node).
*
* Returns an array containing all topics found in the passed array.
* Filters out non-fm:topic nodes.
*/
function getPagedResultsData(nodes, index, count, extractDataFn)
{
var items = new Array();
var i;
var added = 0;
for (i = index; i < nodes.length && added < count; i++)
{
items.push(extractDataFn(nodes[i]));
added++;
}
return ({
"total" : nodes.length,
"pageSize" : count,
"startIndex" : index,
"itemCount" : items.length,
"items": items
});
}
/**
* Returns a list of topics, as returned by the lucene query
*/
function getPagedResultsDataByLuceneQuery(node, luceneQuery, sortAttribute, ascending, index, count, extractDataFn)
{
var nodes = null;
if (sortAttribute != null)
{
nodes = search.luceneSearch(node.nodeRef.storeRef.toString(), luceneQuery, sortAttribute, ascending);
}
else
{
nodes = search.luceneSearch(luceneQuery);
}
return getPagedResultsData(nodes, index, count, extractDataFn);
}

View File

@@ -13,49 +13,49 @@
*/ */
function findNodeInSite() function findNodeInSite()
{ {
var siteId = url.templateArgs.site; var siteId = url.templateArgs.site;
var containerId = url.templateArgs.container; var containerId = url.templateArgs.container;
var path = (url.templateArgs.path != undefined) ? url.templateArgs.path : ""; var path = (url.templateArgs.path != undefined) ? url.templateArgs.path : "";
// fetch site // fetch site
var site = siteService.getSite(siteId); var site = siteService.getSite(siteId);
if (site === null) if (site === null)
{ {
status.setCode(status.STATUS_NOT_FOUND, "Site " + siteId + " does not exist"); status.setCode(status.STATUS_NOT_FOUND, "Site " + siteId + " does not exist");
return null; return null;
} }
// fetch container // fetch container
var node = site.getContainer(containerId); var node = site.getContainer(containerId);
if (node === null) if (node === null)
{ {
status.setCode(status.STATUS_NOT_FOUND, "The container " + containerId + "could not be found in site " + siteId + ". (No write permission?)"); status.setCode(status.STATUS_NOT_FOUND, "The container " + containerId + "could not be found in site " + siteId + ". (No write permission?)");
return null; return null;
} }
// try to fetch the the path is there is any // try to fetch the the path is there is any
if ((path !== null) && (path != "")) if ((path !== null) && (path.length > 0))
{ {
node = node.childByNamePath(path); node = node.childByNamePath(path);
if (node === null) if (node === null)
{ {
status.setCode(status.STATUS_NOT_FOUND, "No node found for the given path: \"" + path + "\" in container " + containerId + " of site " + siteId); status.setCode(status.STATUS_NOT_FOUND, "No node found for the given path: \"" + path + "\" in container " + containerId + " of site " + siteId);
return null; return null;
} }
} }
return node; return node;
} }
function findFromReference() function findFromReference()
{ {
var nodeRef = url.templateArgs.store_type + "://" + url.templateArgs.store_id + "/" + url.templateArgs.id; var nodeRef = url.templateArgs.store_type + "://" + url.templateArgs.store_id + "/" + url.templateArgs.id;
var node = search.findNode(nodeRef); var node = search.findNode(nodeRef);
if (node === null) if (node === null)
{ {
status.setCode(status.STATUS_NOT_FOUND, "Node " + nodeRef + " does not exist"); status.setCode(status.STATUS_NOT_FOUND, "Node " + nodeRef + " does not exist");
} }
return node; return node;
} }
/** /**
@@ -67,21 +67,21 @@ function findFromReference()
*/ */
function getRequestNode() function getRequestNode()
{ {
// check whether we got a node reference or a site related uri // check whether we got a node reference or a site related uri
var node = null; var node = null;
if (url.templateArgs.store_type != undefined) if (url.templateArgs.store_type != undefined)
{ {
node = findFromReference(); node = findFromReference();
} }
// site related uri // site related uri
else if (url.templateArgs.site != undefined) else if (url.templateArgs.site != undefined)
{ {
node = findNodeInSite(); node = findNodeInSite();
} }
else else
{ {
// unknown request params // unknown request params
status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, "Unknown request parameters (webscript incorrectly configured?)"); status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, "Unknown request parameters (webscript incorrectly configured?)");
} }
return node; return node;
} }