SAIL-10 - Data lists in Share (work in progress)

Includes sub-tasks: SAIL-102 - Data list view page, SAIL-103 - Simple render of datagrid, SAIL-105 - Create new list page, SAIL-107 - Edit list as pop-up, SAIL-108 - Delete functionality, SAIL-111 - Filtering, SAIL-114 - Data lists sidebar component

Merged BRANCHES/DEV/DATALISTS to HEAD
   17779: First cut of datalist prototype
   17877: Addition of icons, lists sidebar and first cut of new list dialog

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@19389 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Mike Hatfield
2010-03-19 10:12:07 +00:00
parent ad616fc014
commit 877c723a2b
22 changed files with 1367 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
<#macro resultsJSON results>
<#escape x as jsonUtils.encodeJSONString(x)>
{
"totalResults": ${results?size},
"overallSuccess": ${overallSuccess?string},
"successCount": ${successCount},
"failureCount": ${failureCount},
"results":
[
<#list results as r>
{
<#list r?keys as key>
<#assign value = r[key]>
<#if value?is_number || value?is_boolean>
"${key}": ${value?string}<#if key_has_next>,</#if>
<#else>
"${key}": "${value}"<#if key_has_next>,</#if>
</#if>
</#list>
}<#if r_has_next>,</#if>
</#list>
]
}
</#escape>
</#macro>

View File

@@ -0,0 +1,181 @@
/**
* Copyright (C) 2005-2010 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/>.
*/
/**
* Data List Component: action
*
* For a single-asset action, template paramters address the item.
* For multi-item actions, optional template parameters address the source or destination node,
* and a JSON body addresses the items involved in the action.
*
* @param uri {string} node/{store_type}/{store_id}/{id}
*/
/**
* Main script entry point
* @method main
*/
function main()
{
var nodeRef = null,
rootNode = null,
params = {};
if (url.templateArgs.store_type !== null)
{
/**
* nodeRef input: store_type, store_id and id
*/
var storeType = url.templateArgs.store_type,
storeId = url.templateArgs.store_id,
id = url.templateArgs.id;
nodeRef = storeType + "://" + storeId + "/" + id;
rootNode = resolveVirtualNodeRef(nodeRef);
if (rootNode == null)
{
rootNode = search.findNode(nodeRef);
if (rootNode === null)
{
status.setCode(status.STATUS_NOT_FOUND, "Not a valid nodeRef: '" + nodeRef + "'");
return null;
}
}
params.nodeRef = nodeRef;
params.rootNode = rootNode;
}
// Multiple input files in the JSON body?
var items = getMultipleInputValues("nodeRefs");
if (typeof items != "string")
{
params.items = items;
}
// Check runAction function is provided the action's webscript
if (typeof runAction != "function")
{
status.setCode(status.STATUS_BAD_REQUEST, "Action webscript must provide runAction() function.");
return;
}
// Actually run the action
var results = runAction(params);
if (results)
{
if (typeof results == "string")
{
status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, results);
}
else if (typeof results.status == "object")
{
// Status fields have been manually set
status.redirect = true;
for (var s in results.status)
{
status[s] = results.status[s];
}
}
else
{
/**
* NOTE: Webscripts run within one transaction only.
* If a single operation fails, the transaction is marked for rollback and all
* previous (successful) operations are also therefore rolled back.
* We therefore need to scan the results for a failed operation and mark the entire
* set of operations as failed.
*/
var overallSuccess = true,
successCount = 0,
failureCount = 0;
for (var i = 0, j = results.length; i < j; i++)
{
overallSuccess = overallSuccess && results[i].success;
results[i].success ? ++successCount : ++failureCount;
}
model.overallSuccess = overallSuccess;
model.successCount = successCount;
model.failureCount = failureCount;
model.results = results;
}
}
}
/**
* Resolve "virtual" nodeRefs into nodes
*
* @method resolveVirtualNodeRef
* @param virtualNodeRef {string} nodeRef
* @return {ScriptNode|null} Node corresponding to supplied virtual nodeRef. Returns null if supplied nodeRef isn't a "virtual" type
*/
resolveVirtualNodeRef: function ParseArgs_resolveVirtualNodeRef(nodeRef)
{
var node = null;
if (nodeRef == "alfresco://company/home")
{
node = companyhome;
}
else if (nodeRef == "alfresco://user/home")
{
node = userhome;
}
else if (nodeRef == "alfresco://sites/home")
{
node = companyhome.childrenByXPath("st:sites")[0];
}
return node;
}
/**
* Get multiple input values
*
* @method getMultipleInputValues
* @return {array|string} Array containing multiple values, or string error
*/
function getMultipleInputValues(param)
{
var values = [],
error = null;
try
{
// Was a JSON parameter list supplied?
if (typeof json != "undefined")
{
if (!json.isNull(param))
{
var jsonValues = json.get(param);
// Convert from JSONArray to JavaScript array
for (var i = 0, j = jsonValues.length(); i < j; i++)
{
values.push(jsonValues.get(i));
}
}
}
}
catch(e)
{
error = e.toString();
}
// Return the values array, or the error string if it was set
return (error !== null ? error : values);
}

View File

@@ -0,0 +1,8 @@
<webscript>
<shortname>duplicate</shortname>
<description>Data List Action - Duplicate single or multiple items</description>
<url>/slingshot/datalists/action/duplicate/node/{store_type}/{store_id}/{id}</url>
<format default="json">argument</format>
<authentication>user</authentication>
<transaction>required</transaction>
</webscript>

View File

@@ -0,0 +1,2 @@
<#import "action.lib.ftl" as actionLib />
<@actionLib.resultsJSON results=results />

View File

@@ -0,0 +1,77 @@
<import resource="classpath:/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/action/action.lib.js">
/**
* Copyright (C) 2005-2010 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/>.
*/
/**
* Duplicate multiple items action
* @method POST
*/
/**
* Entrypoint required by action.lib.js
*
* @method runAction
* @param p_params {object} Object literal containing items array
* @return {object|null} object representation of action results
*/
function runAction(p_params)
{
var results = [],
items = p_params.items,
item, result, nodeRef;
// Must have array of items
if (!items || items.length == 0)
{
status.setCode(status.STATUS_BAD_REQUEST, "No items.");
return;
}
for (item in items)
{
nodeRef = items[item];
result =
{
nodeRef: nodeRef,
action: "duplicateItem",
success: false
}
try
{
itemNode = search.findNode(nodeRef);
if (itemNode != null)
{
// TODO: DUPLICATE NODE
}
}
catch (e)
{
result.success = false;
}
results.push(result);
}
return results;
}
/* Bootstrap action script */
main();

View File

@@ -0,0 +1,8 @@
<webscript>
<shortname>files</shortname>
<description>Data List Action - Delete single or multiple items</description>
<url>/slingshot/datalists/action/items</url>
<format default="json">argument</format>
<authentication>user</authentication>
<transaction>required</transaction>
</webscript>

View File

@@ -0,0 +1,2 @@
<#import "action.lib.ftl" as actionLib />
<@actionLib.resultsJSON results=results />

View File

@@ -0,0 +1,77 @@
<import resource="classpath:/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/action/action.lib.js">
/**
* Copyright (C) 2005-2010 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/>.
*/
/**
* Delete multiple items action
* @method DELETE
*/
/**
* Entrypoint required by action.lib.js
*
* @method runAction
* @param p_params {object} Object literal containing items array
* @return {object|null} object representation of action results
*/
function runAction(p_params)
{
var results = [],
items = p_params.items,
item, result, nodeRef;
// Must have array of items
if (!items || items.length == 0)
{
status.setCode(status.STATUS_BAD_REQUEST, "No items.");
return;
}
for (item in items)
{
nodeRef = items[item];
result =
{
nodeRef: nodeRef,
action: "deleteItem",
success: false
}
try
{
itemNode = search.findNode(nodeRef);
if (itemNode != null)
{
result.success = itemNode.remove();
}
}
catch (e)
{
result.success = false;
}
results.push(result);
}
return results;
}
/* Bootstrap action script */
main();

View File

@@ -0,0 +1,9 @@
<webscript>
<shortname>DataLists - Data retrieval</shortname>
<description>Data Lists Component - retrieve data within a given list</description>
<url>/slingshot/datalists/data/site/{site}/{container}/{list}</url>
<url>/slingshot/datalists/data/node/{store_type}/{store_id}/{id}</url>
<format default="json">argument</format>
<authentication>user</authentication>
<transaction allow="readwrite" buffersize="0">required</transaction>
</webscript>

View File

@@ -0,0 +1,36 @@
<#import "item.lib.ftl" as itemLib />
<#escape x as jsonUtils.encodeJSONString(x)>
{
"totalRecords": ${data.paging.totalRecords?c},
"startIndex": ${data.paging.startIndex?c},
"metadata":
{
"parent":
{
<#if data.parent??>
<#assign parentNode = data.parent.node>
"nodeRef": "${parentNode.nodeRef}",
"permissions":
{
"userAccess":
{
<#list data.parent.userAccess?keys as perm>
<#if data.parent.userAccess[perm]?is_boolean>
"${perm?string}": ${data.parent.userAccess[perm]?string}<#if perm_has_next>,</#if>
</#if>
</#list>
}
}
</#if>
}
},
"items":
[
<#list data.items as item>
{
<@itemLib.itemJSON item />
}<#if item_has_next>,</#if>
</#list>
]
}
</#escape>

View File

@@ -0,0 +1,111 @@
<import resource="classpath:/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/evaluator.lib.js">
<import resource="classpath:/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/filters.lib.js">
<import resource="classpath:/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/parse-args.lib.js">
/**
* Copyright (C) 2005-2010 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/>.
*/
/**
* Main entry point: Return data list with properties being supplied in POSTed arguments
*
* @method getData
*/
function getData()
{
// Use helper function to get the arguments
var parsedArgs = ParseArgs.getParsedArgs();
if (parsedArgs === null)
{
return;
}
var fields = null;
// Extract fields (if given)
if (json.has("fields"))
{
// Convert the JSONArray object into a native JavaScript array
fields = [];
var jsonFields = json.get("fields"),
numFields = jsonFields.length();
for (count = 0; count < numFields; count++)
{
fields.push(jsonFields.get(count).replace("_", ":"));
}
}
// Try to find a filter query based on the passed-in arguments
var filter = parsedArgs.filter,
allNodes = [], node,
items = [];
var filterParams = Filters.getFilterParams(filter, parsedArgs)
query = filterParams.query;
// Query the nodes - passing in default sort and result limit parameters
if (query !== "")
{
allNodes = search.query(
{
query: query,
language: filterParams.language,
page:
{
maxItems: (filterParams.limitResults ? parseInt(filterParams.limitResults, 10) : 0)
},
sort: filterParams.sort,
templates: filterParams.templates,
namespace: (filterParams.namespace ? filterParams.namespace : null)
});
for each (node in allNodes)
{
try
{
items.push(Evaluator.run(node, fields));
}
catch(e) {}
}
}
return (
{
fields: fields,
luceneQuery: query,
paging:
{
totalRecords: items.length,
startIndex: 0
},
parent:
{
node: parsedArgs.listNode,
userAccess:
{
create: parsedArgs.listNode.hasPermission("CreateChildren")
}
},
items: items
});
}
/**
* Document List Component: doclist
*/
model.data = getData();

View File

@@ -0,0 +1,205 @@
/**
* Copyright (C) 2005-2010 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/>.
*/
var Evaluator =
{
/**
* Cache for cm:person objects
*/
PeopleObjectCache: {},
/**
* Gets / caches a person object
*
* @method getPersonObject
* @param nodeRef {string} NodeRef of a cm:person object
*/
getPersonObject: function Evaluator_getPersonObject(nodeRef)
{
if (nodeRef == null || nodeRef == "")
{
return null;
}
if (typeof Evaluator.PeopleObjectCache[nodeRef] == "undefined")
{
var person = search.findNode(nodeRef);
Evaluator.PeopleObjectCache[nodeRef] =
{
userName: person.properties.userName,
firstName: person.properties.firstName,
lastName: person.properties.lastName,
displayName: (person.properties.firstName + " " + person.properties.lastName).replace(/^\s+|\s+$/g, "")
};
if (person.assocs["cm:avatar"] != null)
{
Evaluator.PeopleObjectCache[nodeRef].avatar = person.assocs["cm:avatar"][0];
}
}
return Evaluator.PeopleObjectCache[nodeRef];
},
/**
* Cache for nodes that are subtypes of cm:cmobject
*/
ContentObjectCache: {},
/**
* Gets / caches a content object
*
* @method getContentObject
* @param nodeRef {string} NodeRef
*/
getContentObject: function Evaluator_getContentObject(nodeRef)
{
if (nodeRef == null || nodeRef == "")
{
return null;
}
if (typeof Evaluator.ContentObjectCache[nodeRef] == "undefined")
{
var node = search.findNode(nodeRef);
try
{
Evaluator.ContentObjectCache[nodeRef] = node;
}
catch(e)
{
// Possibly a stale indexed node
return null;
}
}
return Evaluator.ContentObjectCache[nodeRef];
},
/**
* Generate displayValue and any extra metadata for this field
*/
decorateFieldData: function Evaluator_decorateFieldData(objData, node)
{
var value = objData.value,
type = objData.type,
displayValue = value,
obj;
if (type == "cm:person")
{
obj = Evaluator.getPersonObject(value);
if (obj != null)
{
displayValue = obj.displayName;
objData.metadata = obj.userName;
}
}
else if (type == "datetime" || type == "date")
{
objData.metadata = type;
}
else if (node.isSubType("cm:folder"))
{
obj = getContentObject(node.nodeRef);
if (obj != null)
{
displayValue = obj.displayPath.substring(companyhome.name.length() + 1);
}
}
else if (node.isSubType("cm:object"))
{
obj = getContentObject(node.nodeRef);
if (obj != null)
{
displayValue = obj.properties["cm:name"];
}
}
objData.displayValue = displayValue;
},
/**
* Node Evaluator - main entrypoint
*/
run: function Evaluator_run(node, fields)
{
var permissions = {},
actionSet = "",
actionLabels = {},
createdBy = Common.getPerson(node.properties["cm:creator"]),
modifiedBy = Common.getPerson(node.properties["cm:modifier"]),
nodeData = {};
/**
* PERMISSIONS
*/
permissions =
{
"create": node.hasPermission("CreateChildren"),
"edit": node.hasPermission("Write"),
"delete": node.hasPermission("Delete")
};
// Use the form service to parse the required properties
scriptObj = formService.getForm("node", node.nodeRef, fields, fields);
// Make sure we can quickly look-up the Field Definition within the formData loop...
var objDefinitions = {};
for each (formDef in scriptObj.fieldDefinitions)
{
objDefinitions[formDef.dataKeyName] = formDef;
}
// Populate the data model
var formData = scriptObj.formData.data;
for (var k in formData)
{
var isAssoc = k.indexOf("assoc") == 0,
value = formData[k].value,
type = isAssoc ? objDefinitions[k].endpointType : objDefinitions[k].dataType,
objData =
{
type: type
};
if (value instanceof java.util.Date)
{
objData.value = utils.toISO8601(value);
}
else
{
// java.util.List instances are returned from ScriptFormData.java as Strings
objData.value = value;
}
Evaluator.decorateFieldData(objData, node);
nodeData[k] = objData;
}
return(
{
node: node,
nodeData: nodeData,
actionSet: actionSet,
actionPermissions: permissions,
createdBy: createdBy,
modifiedBy: modifiedBy,
tags: node.tags,
actionLabels: actionLabels
});
}
};

View File

@@ -0,0 +1,151 @@
/**
* Copyright (C) 2005-2010 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/>.
*/
var Filters =
{
/**
* Create filter parameters based on input parameters
*
* @method getFilterParams
* @param filter {string} Required filter
* @param parsedArgs {object} Parsed arguments object literal
* @return {object} Object literal containing parameters to be used in Lucene search
*/
getFilterParams: function Filter_getFilterParams(filter, parsedArgs)
{
var filterParams =
{
query: "+PARENT:\"" + parsedArgs.nodeRef + "\" ",
limitResults: null,
sort: [
{
column: "@{http://www.alfresco.org/model/content/1.0}name",
ascending: true
}],
language: "lucene",
templates: null
};
// Max returned results specified?
var argMax = args.max;
if ((argMax !== null) && !isNaN(argMax))
{
filterParams.limitResults = argMax;
}
// Create query based on passed-in arguments
var filterData = String(filter.filterData || ""),
filterQuery = filterParams.query;
// Common types and aspects to filter from the UI
var filterQueryDefaults =
" -TYPE:\"{http://www.alfresco.org/model/content/1.0}systemfolder\"" +
" -@cm\\:lockType:READ_ONLY_LOCK";
switch (String(filter.filterId))
{
case "recentlyAdded":
case "recentlyModified":
case "recentlyCreatedByMe":
case "recentlyModifiedByMe":
var onlySelf = (filterData.indexOf("ByMe")) > 0 ? true : false,
dateField = (filterData.indexOf("Created") > 0) ? "created" : "modified",
ownerField = (dateField == "created") ? "creator" : "modifier";
// Default to 7 days - can be overridden using "days" argument
var dayCount = 7,
argDays = args.days;
if ((argDays !== null) && !isNaN(argDays))
{
dayCount = argDays;
}
// Default limit to 50 documents - can be overridden using "max" argument
if (filterParams.limitResults === null)
{
filterParams.limitResults = 50;
}
var date = new Date();
var toQuery = date.getFullYear() + "\\-" + (date.getMonth() + 1) + "\\-" + date.getDate();
date.setDate(date.getDate() - dayCount);
var fromQuery = date.getFullYear() + "\\-" + (date.getMonth() + 1) + "\\-" + date.getDate();
filterQuery = "+PARENT:\"" + parsedArgs.nodeRef;
if (parsedArgs.nodeRef == "alfresco://sites/home")
{
// Special case for "Sites home" pseudo-nodeRef
filterQuery += "/*/cm:dataLists";
}
filterQuery += "\"";
filterQuery += " +@cm\\:" + dateField + ":[" + fromQuery + "T00\\:00\\:00.000 TO " + toQuery + "T23\\:59\\:59.999]";
if (onlySelf)
{
filterQuery += " +@cm\\:" + ownerField + ":" + person.properties.userName;
}
filterQuery += " -TYPE:\"{http://www.alfresco.org/model/content/1.0}folder\"";
filterParams.sort = [
{
column: "@{http://www.alfresco.org/model/content/1.0}" + dateField,
ascending: false
}];
filterParams.query = filterQuery + filterQueryDefaults;
break;
case "createdByMe":
// Default limit to 50 documents - can be overridden using "max" argument
if (filterParams.limitResults === null)
{
filterParams.limitResults = 50;
}
filterQuery = "+PARENT:\"" + parsedArgs.nodeRef;
if (parsedArgs.nodeRef == "alfresco://sites/home")
{
// Special case for "Sites home" pseudo-nodeRef
filterQuery += "/*/cm:dataLists";
}
filterQuery += "\"";
filterQuery += " +@cm\\:modifier:" + person.properties.userName;
filterQuery += " -TYPE:\"{http://www.alfresco.org/model/content/1.0}folder\"";
filterParams.query = filterQuery + filterQueryDefaults;
break;
case "node":
filterParams.query = "+ID:\"" + parsedArgs.nodeRef + "\"";
break;
case "tag":
// Remove any trailing "/" character
if (filterData.charAt(filterData.length - 1) == "/")
{
filterData = filterData.slice(0, -1);
}
filterParams.query += "+PATH:\"/cm:taggable/cm:" + search.ISO9075Encode(filterData) + "/member\"";
break;
default:
filterParams.query = filterQuery + filterQueryDefaults;
break;
}
return filterParams;
}
};

View File

@@ -0,0 +1,67 @@
<#macro itemJSON item>
<#escape x as jsonUtils.encodeJSONString(x)>
<#assign node = item.node>
<#assign tags><#list item.tags as tag>"${tag}"<#if tag_has_next>,</#if></#list></#assign>
"nodeRef": "${node.nodeRef}",
"createdOn": "${xmldate(node.properties.created)}",
"createdBy":
{
"value": "${item.createdBy.userName}",
"displayValue": "${item.createdBy.displayName}"
},
"modifiedOn": "${xmldate(node.properties.modified)}>",
"modifiedBy":
{
"value": "${item.modifiedBy.userName}",
"displayValue": "${item.modifiedBy.displayName}"
},
"actionSet": "${item.actionSet}",
"tags": <#noescape>[${tags}]</#noescape>,
"permissions":
{
"userAccess":
{
<#list item.actionPermissions?keys as actionPerm>
<#if item.actionPermissions[actionPerm]?is_boolean>
"${actionPerm?string}": ${item.actionPermissions[actionPerm]?string}<#if actionPerm_has_next>,</#if>
</#if>
</#list>
}
},
<#if item.custom??>"custom": <#noescape>${item.custom}</#noescape>,</#if>
"actionLabels":
{
<#if item.actionLabels??>
<#list item.actionLabels?keys as actionLabel>
"${actionLabel?string}": "${item.actionLabels[actionLabel]}"<#if actionLabel_has_next>,</#if>
</#list>
</#if>
},
"itemData":
{
<#list item.nodeData?keys as key>
<#assign data = item.nodeData[key]>
"${key}":
{
<#if data.value?is_boolean>
"value": ${data.value?string},
<#elseif data.value?is_number>
"value": ${data.value?c},
<#else>
"value": "${data.value}",
</#if>
<#if data.metadata??>
"metadata": "${data.metadata}",
</#if>
<#if data.displayValue?is_boolean>
"displayValue": ${data.displayValue?string}
<#elseif data.displayValue?is_number>
"displayValue": ${data.displayValue?c}
<#else>
"displayValue": "${data.displayValue}"
</#if>
}<#if key_has_next>,</#if>
</#list>
}
</#escape>
</#macro>

View File

@@ -0,0 +1,8 @@
<webscript>
<shortname>DataLists - Single item data retrieval</shortname>
<description>Data Lists Component - retrieve data for a single item by nodeRef</description>
<url>/slingshot/datalists/item/node/{store_type}/{store_id}/{id}</url>
<format default="json">argument</format>
<authentication>user</authentication>
<transaction allow="readonly">required</transaction>
</webscript>

View File

@@ -0,0 +1,30 @@
<#import "item.lib.ftl" as itemLib />
<#escape x as jsonUtils.encodeJSONString(x)>
{
"metadata":
{
"parent":
{
<#if data.parent??>
<#assign parentNode = data.parent.node>
"nodeRef": "${parentNode.nodeRef}",
"permissions":
{
"userAccess":
{
<#list data.parent.userAccess?keys as perm>
<#if data.parent.userAccess[perm]?is_boolean>
"${perm?string}": ${data.parent.userAccess[perm]?string}<#if perm_has_next>,</#if>
</#if>
</#list>
}
}
</#if>
}
},
"item":
{
<@itemLib.itemJSON data.item />
}
}
</#escape>

View File

@@ -0,0 +1,83 @@
<import resource="classpath:/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/evaluator.lib.js">
<import resource="classpath:/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/parse-args.lib.js">
/**
* Copyright (C) 2005-2010 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/>.
*/
/**
* Main entry point: Return data list with properties being supplied in POSTed arguments
*
* @method getData
*/
function getData()
{
// Use helper function to get the arguments
var parsedArgs = ParseArgs.getParsedArgs();
if (parsedArgs === null)
{
return;
}
var fields = null;
// Extract fields (if given)
if (json.has("fields"))
{
// Convert the JSONArray object into a native JavaScript array
fields = [];
var jsonFields = json.get("fields"),
numFields = jsonFields.length();
for (count = 0; count < numFields; count++)
{
fields.push(jsonFields.get(count).replace("_", ":"));
}
}
// Try to find a filter query based on the passed-in arguments
var node = search.findNode(parsedArgs.nodeRef),
items = [];
if (node != null)
{
try
{
item = Evaluator.run(node, fields);
}
catch(e) {}
}
return (
{
fields: fields,
parent:
{
node: parsedArgs.listNode,
userAccess:
{
create: parsedArgs.listNode.hasPermission("CreateChildren")
}
},
item: item
});
}
/**
* Document List Component: doclist
*/
model.data = getData();

View File

@@ -0,0 +1,11 @@
<#macro listJSON list>
<#escape x as jsonUtils.encodeJSONString(x)>
{
"name": "${list.name}",
"title": "${list.properties.title!list.name}",
"description": "${list.properties.description!""}",
"nodeRef": "${list.nodeRef}",
"itemType": "${list.properties["dl:dataListItemType"]!""}"
}
</#escape>
</#macro>

View File

@@ -0,0 +1,10 @@
<webscript>
<shortname>DataLists</shortname>
<description>Data Lists Component - retrieve data lists within a given site and container, or by container nodeRef</description>
<url>/slingshot/datalists/lists/site/{site}/{container}/</url>
<url>/slingshot/datalists/lists/site/{site}/{container}</url>
<url>/slingshot/datalists/lists/node/{store_type}/{store_id}/{id}</url>
<format default="json">argument</format>
<authentication>user</authentication>
<transaction allow="readwrite" buffersize="0">required</transaction>
</webscript>

View File

@@ -0,0 +1,46 @@
<import resource="classpath:/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/parse-args.lib.js">
/**
* Copyright (C) 2005-2010 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/>.
*/
/**
* Main entry point: Return list of Data Lists
*
* @method getDataLists
*/
function getDataLists()
{
// Use helper function to get the arguments
var parsedArgs = ParseArgs.getParsedArgs();
if (parsedArgs === null)
{
return;
}
return (
{
container: parsedArgs.rootNode,
lists: parsedArgs.rootNode.children
});
}
/**
* Document List Component: doclist
*/
model.datalists = getDataLists();

View File

@@ -0,0 +1,12 @@
<#import "list.lib.ftl" as listLib />
<#escape x as jsonUtils.encodeJSONString(x)>
{
"container": "${datalists.container.nodeRef?string}",
"datalists":
[
<#list datalists.lists as list>
<@listLib.listJSON list /><#if list_has_next>,</#if>
</#list>
]
}
</#escape>

View File

@@ -0,0 +1,208 @@
/**
* Copyright (C) 2005-2010 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/>.
*/
var Common =
{
/**
* Cache for person objects
*/
PeopleCache: {},
/**
* Gets / caches a person object
*
* @method getPerson
* @param username {string} User name
*/
getPerson: function Common_getPerson(username)
{
if (username == null || username == "")
{
return null;
}
if (typeof Common.PeopleCache[username] == "undefined")
{
var person = people.getPerson(username);
if (person == null && username == "System")
{
person =
{
properties:
{
userName: "System",
firstName: "System",
lastName: "User"
},
assocs: {}
};
}
Common.PeopleCache[username] =
{
userName: person.properties.userName,
firstName: person.properties.firstName,
lastName: person.properties.lastName,
displayName: (person.properties.firstName + " " + person.properties.lastName).replace(/^\s+|\s+$/g, "")
};
if (person.assocs["cm:avatar"] != null)
{
Common.PeopleCache[username].avatar = person.assocs["cm:avatar"][0];
}
}
return Common.PeopleCache[username];
}
};
var ParseArgs =
{
/**
* Get and parse arguments
*
* @method getParsedArgs
* @param containerType {string} Optional: Node Type of container to create if it doesn't exist, defaults to "cm:folder"
* @return {array|null} Array containing the validated input parameters
*/
getParsedArgs: function ParseArgs_getParsedArgs(containerType)
{
var rootNode = null,
nodeRef = null,
listNode = null;
if (url.templateArgs.store_type !== null)
{
/**
* nodeRef input: store_type, store_id and id
*/
var storeType = url.templateArgs.store_type,
storeId = url.templateArgs.store_id,
id = url.templateArgs.id;
nodeRef = storeType + "://" + storeId + "/" + id;
rootNode = ParseArgs.resolveVirtualNodeRef(nodeRef);
if (rootNode == null)
{
rootNode = search.findNode(nodeRef);
if (rootNode === null)
{
status.setCode(status.STATUS_NOT_FOUND, "Not a valid nodeRef: '" + nodeRef + "'");
return null;
}
}
listNode = rootNode;
}
else
{
/**
* Site and container input
*/
var siteId = url.templateArgs.site,
containerId = url.templateArgs.container,
listId = url.templateArgs.list,
siteNode = siteService.getSite(siteId);
if (siteNode === null)
{
status.setCode(status.STATUS_NOT_FOUND, "Site not found: '" + siteId + "'");
return null;
}
rootNode = siteNode.getContainer(containerId);
if (rootNode === null)
{
rootNode = siteNode.createContainer(containerId, containerType || "cm:folder");
if (rootNode === null)
{
status.setCode(status.STATUS_NOT_FOUND, "Data Lists container '" + containerId + "' not found in '" + siteId + "'. (No permission?)");
return null;
}
rootNode.properties["cm:description"] = "Data Lists";
rootNode.save();
}
listNode = rootNode;
if (listId !== null)
{
listNode = rootNode.childByNamePath(listId);
if (listNode === null)
{
status.setCode(status.STATUS_NOT_FOUND, "List not found: '" + listId + "'");
return null;
}
}
}
// Filter
var filter = null;
if (args.filter)
{
filter =
{
filterId: args.filter
};
}
else if (typeof json !== "undefined" && json.has("filter"))
{
filter = jsonUtils.toObject(json.get("filter"));
if (filter == null)
{
filter =
{
filterId: ""
}
}
}
var objRet =
{
rootNode: rootNode,
listNode: listNode,
nodeRef: String(listNode.nodeRef),
filter: filter
};
return objRet;
},
/**
* Resolve "virtual" nodeRefs into nodes
*
* @method resolveVirtualNodeRef
* @param virtualNodeRef {string} nodeRef
* @return {ScriptNode|null} Node corresponding to supplied virtual nodeRef. Returns null if supplied nodeRef isn't a "virtual" type
*/
resolveVirtualNodeRef: function ParseArgs_resolveVirtualNodeRef(nodeRef)
{
var node = null;
if (nodeRef == "alfresco://company/home")
{
node = companyhome;
}
else if (nodeRef == "alfresco://user/home")
{
node = userhome;
}
else if (nodeRef == "alfresco://sites/home")
{
node = companyhome.childrenByXPath("st:sites")[0];
}
return node;
}
};